Repository navigation
Fix digest mismatch in non-unique secondary index trees - #43
Merged
Merged
Conversation
Non-unique secondary index trees are built from composite keys (source key + 4-byte rid), but TreeBuilder computed entry digests with the source encoding reported by KeyValueList.KeyEncoding, while readers digest through DuplicateKeyEncoding (source part only). The mismatch broke GetAll/GetRange in two ways: - Sources with exact digests (e.g. Int64) enabled OmittedKeys although the composite keys are not recoverable from the digest: every duplicate collides and the rid tiebreaker is lost, so queries return only part of the entries once the tree spans more than one leaf (#41, regression in format 1.4). - Ascii source keys shorter than 8 bytes leaked the rid's leading bytes into the stored digests, so only the rid-0 entry matched the query digest and bound searches stopped at the first digest mismatch (#42, present since key digests were introduced). Redefine KeyValueList.KeyEncoding as the encoding of the keys the list enumerates: DuplicateKeyValueList overrides it with DuplicateKeyEncoding, which digests the source part only and never reports exact digests, so TreeBuilder computes digests the same way readers do and duplicate-key trees never omit key bytes. Fixes #41 Fixes #42 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hadashiA
force-pushed
the
claude/fix-nonunique-index-digests
branch
from
November 7, 2026 03:37
84140ea to
e32a764
Compare
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.
Fixes #41
Fixes #42
Problem
Non-unique secondary index trees are built from composite keys (source key + 4-byte rid), but
TreeBuildercomputed entry digests with the source encoding reported byKeyValueList.KeyEncoding, while the read side (NonUniqueSecondaryIndexQuery) digests throughDuplicateKeyEncoding, which strips the rid first. The two digest functions disagreed, which brokeGetAll/GetRangein two ways:omitKeyswas decided from the source encoding'sIsKeyDigestExact(true for Int64), so composite-key pages dropped their key bytes even though the digest only covers the source part. Every duplicate collides on the same digest and the rid tiebreaker is unrecoverable, so queries return only part of the entries as soon as the tree spans more than one leaf — hence the 157-row threshold (first leaf split at 156 entries per leaf) and1000 → 64(1000 − 6×156)."abcdefg" + rid=1digests asabcdefg\x01). The query digest is computed rid-less, so only the rid-0 entry matches and the bound search stops at the first digest mismatch, returning 1 row. 8-byte-or-longer keys fill the digest before the rid, which is why they were unaffected. The same leak also un-sorts the stored digest array once a key has more than 256 duplicates (rid carry), which this fix covers as well.Fix
Redefine
KeyValueList.KeyEncodingas the encoding of the keys the list actually enumerates:DuplicateKeyValueListoverrides it withDuplicateKeyEncoding, which digests the source part only and keeps the defaultIsKeyDigestExact = false, soTreeBuildercomputes digests exactly the way readers do and duplicate-key trees never omit key bytes. The list's internals (Add, the grouping comparer) keep using the source encoding. One file changed; unique trees are unchanged.Verification
dotnet test: 95/95 passing.Note: databases already built with an affected version need to be rebuilt — the bad digests/omitted keys are baked into the file.
🤖 Generated with Claude Code