Skip to content

Report the rank of each entry when scanning a rank index - #4563

Open
hatyo wants to merge 2 commits into
mainfrom
apple/hatyo/rank-in-index-entry
Open

Report the rank of each entry when scanning a rank index#4563
hatyo wants to merge 2 commits into
mainfrom
apple/hatyo/rank-in-index-entry

Conversation

@hatyo

@hatyo hatyo commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

A scan of a rank index cannot currently tell a caller what rank each entry holds. BY_RANK converts the rank range into a score range and then performs an ordinary by-value scan, so its entries come back byte-for-byte identical to what BY_VALUE yields — the rank is consumed as a bound and then discarded. It is stored in neither the entry key nor the entry value, and a rank index leaves the value empty. Anything that wants to project a rank, rather than merely filter on one, therefore has nothing to read.

This adds RankScanBounds, an IndexScanBounds that accepts BY_VALUE or BY_RANK along with an includeRankAsValue option asking RankIndexMaintainer to report each entry's rank in that empty value. No new lookup was needed: the RANK record function already computed exactly this, and it wanted a record only in order to evaluate the index key against it. Keying it off the index key instead lets a scanned entry be ranked without loading its record, and the record path reduces to producing that key first. The javadoc is explicit that the option is not free — it costs one ranked-set skip-list traversal per entry returned, so the cost grows with entries returned rather than with the range asked for.

@hatyo hatyo added the enhancement New feature or request label Sep 4, 2026
if (!rankScanBounds.includeRankAsValue()) {
return recordCursor;
}
return recordCursor.mapPipelined(indexEntry -> rankFor(indexEntry.getKey())

@hatyo hatyo Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the important piece of this PR, it is admittedly expensive, for each scanned record we probe the secondary subspace, if I am not mistaken, this is about O(log(n)) I/O, per item, where n is the number of elements per partitioning group.

There are perhaps more clever ways to do this, considering that values in the primary subspace are already sorted by score, which means, in theory, we just need to know the (dense) rank of the first element, and derive the (dense) rank of the following items since the score and rank are compatibly ordered, that however requires remembering the previous rank + the previous score, which is problematic because it is sensitive to order of scanned items, which is not guaranteed with mapPipelined with number of workers > 1, and I can't find a correct way for achieving this that doesn't require reduced parallelism and/or heavy synchronization logic that would be probably less compelling than this approach, I might be wrong though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in a world where you only use rankFor() the first item and then just increment, you really don't have to use mapPipelined() anymore as only the first one actually incurs I/O. In other words you could roll your own cursor, that initially farms out work using rankFor() and waits for it and then just increments the rank itself. I think that's better than doing I/O for each item (but now it's pipelined). Again, in the look-up-first-item-then-increment approach you don't need the pipelining.

@hatyo
hatyo marked this pull request as ready for review September 4, 2026 17:44
@hatyo
hatyo force-pushed the apple/hatyo/rank-in-index-entry branch from 080d4f4 to 23f9d00 Compare September 4, 2026 17:47
A rank index scan cannot currently tell a caller what rank each entry
holds. A BY_RANK scan converts the rank range into a score range and
then performs an ordinary by-value scan, so its entries are identical to
those a BY_VALUE scan produces: the rank is consumed as a bound and
never reported. The rank is stored in neither the entry key nor the
entry value, and a rank index leaves the value empty.

This adds RankScanBounds, an IndexScanBounds accepting BY_VALUE or
BY_RANK together with an includeRankAsValue option that asks
RankIndexMaintainer to report each entry's rank in the otherwise empty
value. The rank lookup that the RANK record function already performed
is reused: it needed a record only in order to evaluate the index key
against it, so it is now keyed off the index key itself, which a scan
already has in hand.
@hatyo
hatyo force-pushed the apple/hatyo/rank-in-index-entry branch from 23f9d00 to 9d2344e Compare September 4, 2026 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants