[#33444] DocDB: Add the identity-aware unique-index verification scan - #33601
Open
jmeehan16 wants to merge 1 commit into
Conversation
11 tasks
✅ Deploy Preview for infallible-bardeen-164bc9 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
jmeehan16
marked this pull request as ready for review
August 28, 2026 21:39
The read half of deferred uniqueness verification: a library that scans a unique-index tablet's regular database over the inclusive window [backfill_read_ht, verify_upper_ht] and replays every DocKey group's physical history chronologically, deciding Clean / Violation / Inconclusive per the design document's live-identity state machine. Read-only; no production caller yet (the tserver RPC and master integration follow). The window starts each group from an empty live set because SKIP_ALL backfill unconditionally re-materializes every identity live at backfill_read_ht *at* backfill_read_ht -- stated as a precondition on the options, since any change reintroducing conditional backfill writes invalidates the scan range. Replay order is hybrid time ascending, write ID ascending within one hybrid time -- which orders foreground records (write IDs below the marked-domain floor) before floored backfill records at an equal hybrid time. Storage order within a group is subkey-section-major (all row-level versions, then each column's), not hybrid-time-major, so the default path buffers a group and sorts it chronologically; a group exceeding the buffer bound is replayed by a bounded-memory merge of three reverse section cursors (row-level, liveness column, ybidxbasectid column -- other columns never affect identity state). Within one section a Prev() walk is natively chronological. Physical records assemble into logical events per (DocKey, hybrid time): packed rows and row-level tombstones are self-contained events at their own write IDs (packed V2 rows with kIsUpdateFlag are updates; V1 and unflagged V2 are inserts). Non-packed column records group by write-ID structure: marked records (at or above kBackfillWriteIdFloor) group by exact write ID -- every record of one marked backfill operation shares the operation's Raft-index write ID, so two non-packed backfill candidates surface as two inserts (a Violation) even on packing-disabled clusters -- while unmarked records assemble into one foreground event (intents take consecutive write IDs; two foreground writers of one DocKey at one commit hybrid time would collide physically, and conflicting shapes fail closed). With the liveness column present a group is an insert; a lone ybidxbasectid record is the in-place identity update yb_lsm.c emits for PK updates; other-column-only writes (INCLUDE updates) change nothing. The identity is the decoded ybidxbasectid value, comparable across packed and non-packed shapes. Ambiguous or unknown encodings return Inconclusive, never a guess. State machine: Insert adds (second distinct live identity = Violation; repeated identity = idempotent retry -- marked writes make chunk retries physically visible as same-identity versions); Update replaces, or establishes on an empty set (it cannot mask a duplicate: two window-start identities would both have been re-materialized as inserts); Delete clears the whole set (unique-index tombstones are DocKey-level). Violation/Inconclusive results carry value-free reasons and counts only; the keyed diagnostic fingerprint is deliberately deferred to the observability part, where key management belongs. Pagination is DocKey-aligned: the scan stops on group budget or deadline with an encoded resume key; a group's history is never split across pages. Test Plan: ./yb_build.sh release --cxx-test unique_index_verifier-test 18 cases: empty tablet; single backfill insert; the funnel-duplicate shape (two distinct identities at backfill_read_ht under distinct marked write IDs) in both the packed and the non-packed (packing-disabled) shapes, with the same-identity non-packed variant clean; idempotent retried chunks; foreground-before-backfill at an equal hybrid time (same identity clean, distinct identities violation); insert-delete-insert clean; transient overlap violation; in-place PK update (standalone column shape); packed-V2-with-flag PK update (and unflagged packed insert still violating); packed V1 shapes; update-establishes-on-empty; INCLUDE-column update preserving the live identity; below/above-window records ignored; unknown encoding inconclusive with reason; DocKey-aligned pagination with resume; oversized-group fallback (violation found in a later group, clean oversized group replayed in correct chronological order). Assisted-By: devx/08801cf6-2854-4c6f-a378-1d98d239b22d --- _automated · Claude Fable 5 (opencode)_
jmeehan16
force-pushed
the
feature-stack/Shopify/uniq-idx-4a-verifier-core
branch
from
August 31, 2026 18:36
c09182b to
4dd45c0
Compare
This was referenced Aug 31, 2026
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
The read half of deferred uniqueness verification (#33444):
docdb::VerifyUniqueIndexTabletscans a unique-index tablet's regular database over the inclusive window
[backfill_read_ht, verify_upper_ht]and replays every DocKey group's physical historychronologically, deciding Clean / Violation / Inconclusive per the design document's
live-identity state machine. Read-only library + unit tests; the tserver RPC and the master
coordinator that drive it are the next parts of the stack.
The window starts each group from an empty live set because SKIP_ALL backfill unconditionally
re-materializes every identity live at
backfill_read_htatbackfill_read_ht-- stated asa precondition on the options, since any change reintroducing conditional backfill writes
invalidates the scan range.
Replay order is hybrid time ascending, write ID ascending within one hybrid time -- which
orders foreground records (write IDs below the marked-domain floor, #33553) before floored
backfill records at an equal hybrid time. Storage order within a group is
subkey-section-major (all row-level versions, then each column's), not hybrid-time-major, so
the default path buffers a group and sorts it chronologically; a group exceeding the buffer
bound is replayed by a bounded-memory merge of three reverse section cursors (row-level,
liveness column, ybidxbasectid column -- other columns never affect identity state). Within
one section a Prev() walk is natively chronological.
Physical records assemble into logical events per (DocKey, hybrid time): packed rows and
row-level tombstones are self-contained events at their own write IDs (packed V2 rows with
kIsUpdateFlag are updates; V1 and unflagged V2 are inserts). Non-packed column records group
by write-ID structure: marked records (at or above
kBackfillWriteIdFloor) group by exactwrite ID -- every record of one marked backfill operation shares the operation's Raft-index
write ID, so two non-packed backfill candidates surface as two inserts (a Violation) even on
packing-disabled clusters -- while unmarked records assemble into one foreground event
(intents take consecutive write IDs; two foreground writers of one DocKey at one commit
hybrid time would collide physically, and conflicting shapes fail closed). With the liveness
column present a group is an insert; a lone ybidxbasectid record is the in-place identity
update yb_lsm.c emits for PK updates; other-column-only writes (INCLUDE updates) change
nothing. The identity is the decoded ybidxbasectid value, comparable across packed and
non-packed shapes. Ambiguous or unknown encodings return Inconclusive, never a guess.
State machine: Insert adds (second distinct live identity = Violation; repeated identity =
idempotent retry -- marked writes make chunk retries physically visible as same-identity
versions); Update replaces, or establishes on an empty set (it cannot mask a duplicate: two
window-start identities would both have been re-materialized as inserts); Delete clears the
whole set (unique-index tombstones are DocKey-level).
Violation/Inconclusive results carry value-free reasons and counts only; the keyed diagnostic
fingerprint is deliberately deferred to the observability part, where key management belongs.
Pagination is DocKey-aligned: the scan stops on group budget or deadline with an encoded
resume key; a group's history is never split across pages.
One activation-decision input recorded for the eventual default-on work: packed V1
full-row updates carry no flags byte and are indistinguishable from inserts, so verify-time
correctness requires
ysql_mark_update_packed_rowwheneverpack_full_row_updateisenabled.
The scan's iterators set
fill_cache = false: a one-shot pass over history that regularreads never touch should not evict the working set.
No production caller and no daemon changes: pure library + tests, no performance surface.
Stacked on #33403, #33404, #33484, #33544, #33553, #33580, and #33584, based on
feature-stack/Shopify/uniq-idx-3b-ii-generation-orchestrationper the feature-stackworkflow, so the diff is exactly this PR's own commit. A failing pr-title check on stacked
PRs is a known gap in that check; the stack merges bottom-up and retargets as parents merge.
Test plan
macOS arm64, release -- all green:
unique_index_verifier-test, 18 cases: empty tablet; single backfill insert; thefunnel-duplicate shape (two distinct identities at backfill_read_ht under distinct
marked write IDs) in both the packed and the non-packed (packing-disabled) shapes, with
the same-identity non-packed variant clean; idempotent retried chunks;
foreground-before-backfill at an equal hybrid time (same identity clean, distinct
identities violation); insert-delete-insert clean; transient overlap violation;
in-place PK update (standalone column shape); packed-V2-with-flag PK update (and
unflagged packed insert still violating); packed V1 shapes;
update-establishes-on-empty; INCLUDE-column update preserving the live identity;
below/above-window records ignored; unknown encoding inconclusive with reason;
DocKey-aligned pagination with resume; oversized-group fallback (violation found in a
later group; clean oversized group replayed in correct chronological order).
AlmaLinux (x86_64, clang21) -- all green, re-validated after the stack-wide rework:
unique_index_verifier-testat this PR's own commit (4dd45c0827).unique_index_verifier-testat the stack tip (c69686cd0d), whichcontains this commit; also green under release.