Skip to content

Read ahead one page in full-log ScanCursor iterations - #2177

Closed
Shivam Sharma (ShivamSharma43) wants to merge 1 commit into
microsoft:mainfrom
ShivamSharma43:fix/1436-dbsize-storage-tier
Closed

Shivam Sharma (ShivamSharma43) wants to merge 1 commit into
microsoft:mainfrom
ShivamSharma43:fix/1436-dbsize-storage-tier

Conversation

@ShivamSharma43

Copy link
Copy Markdown

Refs #1436

Problem

DBSIZE (and INFO KEYSPACE, KEYS, and slot deletion) runs a lookup-based ScanCursor over the whole log. ScanCursor always built its iterator with DiskScanBufferingMode.SinglePageBuffering, so for the on-disk part of the log each page was read, processed, and only then was the next page's read issued. Disk IO and the per-record liveness checks never overlapped.

Change

ScanCursor (in SpanByteAllocatorImpl and ObjectAllocatorImpl) now uses DoublePageBuffering when the caller asks for the whole log (count == long.MaxValue), so the next page is read while the current one is processed. Bounded-count calls such as SCAN keep SinglePageBuffering: they create a new iterator per call and usually use only part of one page, so read-ahead would be wasted IO.

Measurements

DBSIZE against GarnetServer with --storage-tier --memory 64m --page 4m --index 64m/256m --no-obj, recovered from a checkpoint so the log is almost entirely below HeadAddress. Windows 11, consumer NVMe (KIOXIA KBG40). Old and new builds alternated between runs to reduce noise:

Dataset Before (median) After (median)
60K keys × 128 KB values (7.8 GB log) ~9.8 s ~6.8 s
4M keys × 64 B values (316 MB log) ~2.0 s ~1.8 s

I also tried 4 frames of read-ahead; it was no better than 2 on this hardware, so this PR uses the existing DoublePageBuffering mode.

Scope relative to #1436

This does not make DBSIZE O(1). With the storage tier enabled it is still a full scan whose cost is proportional to the log size (in the issue's setup, 256 KB values stored inline, it is mostly a sequential read of the whole log). An exact running key count seems hard to maintain, because a blind upsert on a key whose previous version is on disk does not know whether the key already existed. Happy to follow up if you'd prefer an approximate or cached DBSIZE for storage-tier deployments.

The INFO keyspace part of the issue appears to be covered already by #1919 (INFO KEYSPACE, which is deliberately excluded from plain INFO).

Testing

  • Tsavorite.test.hlog and Tsavorite.test.recovery, filtered to Iteration|Scan: 81/81 and 77/77 passed (15 skipped as before). These include ScanCursor(count: long.MaxValue) over fully evicted multi-page logs, with and without forced hash collisions.
  • Garnet.test filtered to RespScanCommandsTests|RespInfoTests|DbSize|Keys|RespRecoveryFailOpenTests: 118/118 passed.
  • dotnet format whitespace --verify-no-changes is clean for the changed files.

🤖 Generated with Claude Code

ScanCursor always used SinglePageBuffering, so a whole-log lookup scan
(DBSIZE, INFO KEYSPACE, KEYS, slot deletion) waited for each on-disk page
to load before processing it, then waited again for the next one. Use
DoublePageBuffering when the caller asks for the whole log
(count == long.MaxValue) so the next page is read while the current page's
records are liveness-checked. Bounded-count calls such as SCAN keep
SinglePageBuffering, since they create a new iterator per call and would
waste the read-ahead.

DBSIZE with --storage-tier, 64 MB memory, 4 MB pages, log almost all on
disk (Windows, NVMe), alternating A/B runs:
- 60K keys x 128 KB (7.8 GB log): ~9.8 s -> ~6.8 s median
- 4M keys x 64 B (316 MB log):    ~2.0 s -> ~1.8 s median

Refs microsoft#1436

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Copilot AI balanced review requested due to automatic review settings September 28, 2026 12:44

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@TedHartMS

Copy link
Copy Markdown
Contributor

Thanks for the contribution! This has been consolidated into PR #2181

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants