[INF-1722] Verify source rows and audit deferred CDC progress - #23
Merged
Conversation
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
Keep verification source → target only, defer CDC mismatches once for one minute, confirm replay has passed a fresh source snapshot, and require target convergence or advancement. A source change is not an automatic skip. There is no retry/defer loop.
Add optional, explicitly audited verification-only app exclusions via
--verify-ignore-appsand the controller settings. No app IDs are hardcoded and replication is unchanged.All observed mismatches and final decisions are retained in a durable audit log, including mismatches accepted as progressing.
CDC approach
xmin; immediately audit every mismatch. Initially absent source keys are ignored, including target-only rows and recorded deletes. Reinserted source keys are checked normally.converged; equality is checked firstadvanced, reported separately from equalityadvanced, reported separately from equalitytarget_stalled; do not skip because the source changedCheck equality before advancement. Replay can catch up between the initial source read and the initial target read. At the deferred check, both rows can already match even though the target has not changed since its first snapshot. A stable fresh match passes without requiring a redundant target write. Source changes during the bracketed reads still prevent a verified-equal verdict.
Advancement means a target row appeared or its hash/
xminchanged relative to its original target snapshot. Transaction IDs are compared only within the same database, never sourcexminversus targetxmin. A target deletion counts as advancement only when the source also disappeared; losing a target row still required by the source is not advancement. Advancement is required only when a stable match was not established.An advancing target is accepted by this live CDC check even if it is not yet equal to the source. That outcome is explicit in the audit and summary, not mislabeled as a matched row.
Exactly one deferred recheck
Each initial mismatch gets one recheck after the one-minute delay. Once classified, it is finished: advancing rows are accepted, stalled rows fail, and nothing is requeued. There is no retry cap or indefinite loop, including when table timeouts are disabled.
The delay and queue time are outside
--verify-table-timeout; active reads and replay confirmation share the table's remaining budget. Confirmation is also bounded by--verify-converge-timeout(default one minute), even with table timeouts disabled. If replay does not reach the marker, the check ends incomplete with the pending keys audited. This is one bounded confirmation, not another retry/defer loop. A dedicated worker uses at most one additional source/target connection pair. Heap-sample verification retains its existing WAL-marker retry rule.Verification app scope
--verify-ignore-apps 7,42, or controllerverify_ignore_apps, excludes mismatches owned by those sourceapp_pkvalues from verification verdicts. Empty clears the setting. IDs must be positive bigint values; whitespace and duplicates are normalized for verification.app_pkis in the key or an ordinary column. Tables withoutapp_pk, NULL source app values, and other apps are still checked. Target ownership cannot exempt a source row. Fresh source ownership is checked again during rechecks.ignored_appaudit records withapp_idand snapshots. Run headers record the exclusion list; result summaries report ignored observations and the controller shows the configured scope. Heap/CDC may observe the same key, soignored_rowsis an observation count, not a unique-row count.Durable audit
Append to
<dir>/log/verify-audit.jsonl, separate from the controller's bounded output buffer:converged,advanced,unresolved,ignored_app, orincomplete, with a separatesource_changedflag and replay confirmation boundary when established.Batches are synced before proceeding; an audit-write failure aborts verification. The file is created with mode
0600and contains sensitive keys/comparison metadata, not full row contents. Retain it on durable storage; it is not automatically truncated or rotated.The dashboard distinguishes pending checks, confirmed failures, and incomplete checks. Pending CDC work does not hide existing heap failures. Summaries separate matched rows, target advancement, source changes, and pending candidates.
Validation
go test ./...go vet ./...go test -race ./...go test -race -tags=integration ./internal/verify -count=1— PostgreSQL 17node --test internal/controller/ui_test.mjs— nine dashboard tests, also added to CITests cover target supersets; missing/different rows; one-minute default; delayed convergence; source updates, deletion, no-op writes, reverts and reinserts still mismatched without target advancement failing; simultaneous source/target advancement; fresh matching reads after both change; already-matching unchanged targets; replay catching up between the initial source and target reads; source changes during target reads; target advancement accepted after exactly one check; stalled targets; mixed matched/advancing/stalled keys; cancellation/timeouts; audit failures; all-mismatch logging; and concurrent append-only audit records across runs. The equality-precedence regression cases reproduced
target_stalledbefore the correction. Tests shorten the delay internally; the application uses one minute.Additional PostgreSQL tests cover convergence/advancement only after replay confirmation, confirmed stalls, bounded confirmation timeout without a table timeout, marker/wait errors and cancellation, source app exclusions in both key layouts and both strata, NULL/missing app columns, target ownership isolation, source ownership changes during rechecks, and audit failure for excluded mismatches. Config/CLI/controller tests cover validation, clearing, partial updates, persistence and the visible scope banner.
Scope / rollout
This remains sampled verification, not proof of full database equality. Initial target-only rows and unapplied deletes are outside the one-way check. A passing live CDC check may include advancing targets whose equality has not been established.
No CDC replay or database row changes and no deployment manifests in this PR. Verification requires a writable audit log. Back up controller settings before upgrade; rolling back to a binary without the new config field requires restoring the prior settings file or removing that field. No exclusion is enabled by default.