fix(dns): pair retained values before position in the set plan - #257
Open
AshutoshMore142k4 wants to merge 1 commit into
Open
fix(dns): pair retained values before position in the set plan#257AshutoshMore142k4 wants to merge 1 commit into
AshutoshMore142k4 wants to merge 1 commit into
Conversation
`plan_set` paired existing records to `--data` values purely by position,
so a value that was staying in the set could be paired with a different
record and re-created. v3 keys record identity on (name, type, data), so
that create fails with DUPLICATE_RECORD — and `apply_replace` keeps the
old record when its create fails, while the surplus `Delete` later in the
plan still runs.
Narrowing `www A {1.2.3.4, 5.6.7.8}` to just 5.6.7.8 therefore deleted
5.6.7.8 and left 1.2.3.4 behind — the exact inverse of what was asked.
A pure reorder of the same values failed both replaces as duplicates.
Pair each desired value with the record already holding it first, then
pair whatever is left by position as before. `plan_set` now takes
(record_id, current value) so it can see content.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Retained records must be matched using full type-specific identity to avoid incorrect replacement and deletion.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR updates DNS set reconciliation to preserve retained records before positional replacement, preventing duplicate-create failures during narrowing and reordering.
Changes:
- Updates
plan_setpairing and planner inputs. - Updates command and dry-run tests.
- Adds regression coverage for narrowing, reordering, mixed changes, and duplicates.
File summaries
| File | Summary |
|---|---|
rust/src/dns/set/plan.rs |
Implements retained-value pairing and tests; critical issue remains because matching ignores type-specific identity fields. |
rust/src/dns/set/outcome.rs |
Updates dry-run plan tests for the new planner inputs. |
rust/src/dns/set/mod.rs |
Supplies record values to planning; the same critical full-identity matching issue remains. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| .filter_map(|r| { | ||
| Some(( | ||
| r.record_id.clone()?, | ||
| record_value(r).unwrap_or_default().to_owned(), |
Comment on lines
+43
to
+46
| for (d, want) in desired.iter().enumerate() { | ||
| if let Some(e) = (0..existing.len()).find(|&e| !taken[e] && &existing[e].1 == want) { | ||
| taken[e] = true; | ||
| paired[d] = Some(e); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
plan_setpaired existing records to--datavalues purely by position, so a value that was staying in the set could get paired with a different record and re-created. v3 keys record identity on (name, type, data), so that create fails withDUPLICATE_RECORD— andapply_replacedeliberately keeps the old record when its create fails, while the surplusDeletelater in the plan still runs unconditionally.Net effect on a narrowing set: given
www A {1.2.3.4, 5.6.7.8}, runningplanned
Replace{r1 → 5.6.7.8},Delete{r2}. The replace's create collided with r2's still-live5.6.7.8, so r1 was kept; the delete then removed r2. The zone ended up holding1.2.3.4— the value the user asked to drop — with5.6.7.8gone. The command does exit non-zero, but the zone is already inverted by then.A pure reorder (
--data 5.6.7.8 --data 1.2.3.4against the same zone) is the same root cause with a milder symptom: both replaces fail as duplicates and the command errors having changed nothing.Fix: pair each desired value with the record that already holds it first, then pair whatever is left by position exactly as before. A value that stays in the set is therefore never re-created, so the collision can't arise.
plan_setnow takes(record_id, current value)so it can see content; ordering of the emitted plan (replaces → deletes → creates) is unchanged.Ordering deletes before creates would also dodge the collision, but it opens a window where the name resolves to nothing — so this keeps the existing create-then-delete shape.
Test plan
cargo check --workspacecargo fmt --checkcargo test --workspace— 780 passed./rust/scripts/check-module-size.shcargo clippy --workspace --all-targets -- -D warnings— clean for this change. (Unrelated pre-existing note: on a Windows host it errors on an unuseduse super::*inextension/security/file_discovery.rs, whose only two tests are#[cfg(unix)], so that test module compiles empty there. CI on ubuntu is unaffected.)plan_setcases: the narrowing case above, a pure reorder, a mixed keep-one/replace-one, and duplicate--datavalues pairing to distinct recordsplan_setanddry_run_set_previewtests updated for the new parameter shape and still assert the same plans