perf: stream equality and range index scans instead of materializing every id - #1298
perf: stream equality and range index scans instead of materializing every id#1298brettwooldridge wants to merge 1 commit into
Conversation
…every id NitriteIndexer.findByFilter returns a LinkedHashSet of every matching id, so find(k = v).firstOrNull() built the whole match set before handing back one row, and a bounded page paid for the entire result. On a non-unique index over a low-cardinality field that set is a large fraction of the collection on every lookup. The composite layout already keeps its rows in key order, so the two plan shapes that map onto one bounded walk of it, an equality on the indexed field and a two-sided range on it, are now served by a lazy iterator that starts at the first key inside the bounds and stops at the first key outside them. It honours the plan's reverse scan order by visiting the key groups backwards while reading each group forwards, exactly as the materialized scan orders them, skips entries removed in an open transaction, and returns a document indexed under several keys once. NitriteIndex.findNitriteIdStream and NitriteIndexer.findByFilterStream are new default methods returning null, so every other index type, plugin indexer and plan shape keeps the materialized path unchanged. ReadOperations prefers the stream when one is offered; the covered-count shortcut that lets size() answer without fetching documents is kept by counting the streamed ids on demand, so size() still reads the index only. Tests compare the stream with the materialized scan for equality, range and reverse order, check the shapes it declines, show with a spied map that only one key is read for the first row, and exercise counts, paging, descending order, multi-valued fields and removals through the public API. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 53 minutes. View limit detailsLimit details: You’ve used all 4 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (9)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Superseded by #1304 — I don't have push access to your fork, so the rebase went to a branch on the upstream repo. Same commits, authorship preserved, plus:
Local on the rebase: |
NitriteIndexer.findByFilter returns a LinkedHashSet of every matching id, so find(k = v).firstOrNull() built the whole match set before handing back one row, and a bounded page paid for the entire result. On a non-unique index over a low-cardinality field that set is a large fraction of the collection on every lookup.
The composite layout already keeps its rows in key order, so the two plan shapes that map onto one bounded walk of it, an equality on the indexed field and a two-sided range on it, are now served by a lazy iterator that starts at the first key inside the bounds and stops at the first key outside them. It honours the plan's reverse scan order by visiting the key groups backwards while reading each group forwards, exactly as the materialized scan orders them, skips entries removed in an open transaction, and returns a document indexed under several keys once.
NitriteIndex.findNitriteIdStream and NitriteIndexer.findByFilterStream are new default methods returning null, so every other index type, plugin indexer and plan shape keeps the materialized path unchanged. ReadOperations prefers the stream when one is offered; the covered-count shortcut that lets size() answer without fetching documents is kept by counting the streamed ids on demand, so size() still reads the index only.
Tests compare the stream with the materialized scan for equality, range and reverse order, check the shapes it declines, show with a spied map that only one key is read for the first row, and exercise counts, paging, descending order, multi-valued fields and removals through the public API.