perf(db): harden unchanged index updates#1
Merged
K-Mistele merged 1 commit intoJul 24, 2026
Merged
Conversation
3 tasks
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
This stacked follow-up hardens TanStack#1691’s unchanged-index fast path and cuts more of its residual work. It repairs
BasicIndexkey bookkeeping after a failed removal, caches compiled index evaluators, avoids object normalization work for primitives, and adds model-based property tests for both index types.Root cause
BasicIndex.remove()preserves the value bucket when expression evaluation fails, but removes the key fromindexedKeys. A later same-value update saw the key in the bucket and returned early, sokeyCountstayed wrong.The no-op update path also recompiled the same index expression and sent primitive values through object-specific normalization checks on every update.
Approach
indexedKeysmembership before treating aBasicIndexupdate as a no-op.normalizeValue()before the object-specific checks.BasicIndexandBTreeIndex, then compare their buckets, keys, ordering, lookups, range queries, and rebuilt state with a reference model after every action.Key invariants
keyCount,indexedKeysSet, value buckets, ordered entries, equality lookups, and range queries must agree.Non-goals
oldItemvalues are handled; fix(db): a key in BasicIndex/BTreeIndex lives in at most one bucket TanStack/db#1517 owns that invariant.Trade-offs
The evaluator cache and primitive fast path overlap two small parts of TanStack#1645. Keeping them here makes the complete unchanged-update optimization reviewable and testable as one stack, at the cost of dropping those hunks when useful pieces of TanStack#1645 are cherry-picked later.
Verification
The focused suite passes 23 tests. The full package suite passes 2,483 tests with 5 skipped, and the package build passes.
Files changed
base-index.ts: cache the compiled index evaluator.basic-index.ts: include key-set membership in the no-op guard.comparison.ts: return primitives before object normalization.index-update-short-circuit.test.ts: cover failed evaluation and bookkeeping repair.index-update.property.test.ts: compare randomized valid operation sequences with a reference model.quick-trees-rest.md: describe the full patch impact.Stacked on TanStack#1691. Related: TanStack#1645 and TanStack#1517.