fix: add async prefetch to iterScanner.Next() for paged queries#2
Open
fix: add async prefetch to iterScanner.Next() for paged queries#2
Conversation
The iterScanner.Next() method was missing the async prefetch trigger that Iter.Scan() has had since commit 5820f12 added the Scanner interface. This meant that callers using the Scanner API (iter.Scanner().Next()) never got background prefetching of the next page, causing a full synchronous fetch stall at page boundaries. Add the same prefetch trigger to iterScanner.Next(): after reading all columns but before incrementing iter.pos, check if we have crossed the prefetch threshold (default 75% through the page) and if so, call iter.nextIter.fetchAsync() to start fetching the next page in the background. The 4-line fix mirrors the existing logic in Iter.Scan() at session.go lines 1940-1943. Benchmark results (vs master, 10 iterations): Scanner path (scanner_prefetch_test.go): IterScanner_Next: 114.9µs → 112.7µs ~ (p=0.684, unchanged) IterScan: 302.9µs → 176.3µs -41.78% (p=0.000) IterScanner_NextNoNextIter: 100.9µs → 46.4µs -54.01% (p=0.000) Allocations: +1 alloc for IterScanner_Next (1019 → 1020, the sync.Once goroutine for prefetch). Zero additional bytes. Conn path (conn_test.go -tags unit): SingleConn: 42.47µs → 55.93µs +31.69% (p=0.043) — baseline has ±41% variance, likely noise from the TCP test server. Zero alloc change. Tests: 5 unit tests + 3 benchmarks in scanner_prefetch_test.go.
c4a00fa to
0985b32
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.
Summary
The
iterScanner.Next()method was missing the async prefetch trigger thatIter.Scan()has had since commit5820f12added the Scanner interface. This meant callers using the Scanner API (iter.Scanner().Next()) never got background prefetching of the next page, causing a full synchronous fetch stall at page boundaries.The Bug
When using paged queries through the Scanner interface:
The
Iter.Scan()path has had async prefetch since the beginning — when 75% of the current page is consumed, it triggersnextIter.fetchAsync()to start fetching the next page in a background goroutine. The Scanner interface (added in5820f12) never got this optimization, so it always stalled at page boundaries.The Fix
4 lines added to
iterScanner.Next()insession.go, mirroring the existing logic inIter.Scan()(lines 1940-1943):This is placed after reading all columns but before
iter.pos++, matching the exact position in theIter.Scan()flow.Changes
iterScanner.Next()(around line 1823)Benchmarks
Benchstat comparison against master (10 iterations each,
benchstatv0.12).Scanner benchmarks (
scanner_prefetch_test.go)Time/op
Bytes/op
Allocs/op
Notes:
sync.Oncegoroutine for the prefetch.nextItersetup in the benchmark.nextIter.BenchmarkSingleConn (
conn_test.go,-tags unit)The baseline has ±41% variance (TCP test server noise). No alloc changes. Not a real regression.
Tests
5 unit tests:
TestIterScannerPrefetchTrigger— verifiesfetchAsync()is called when position crosses thresholdTestIterScannerPrefetchNotTriggeredBeforeThreshold— verifies no prefetch before thresholdTestIterScannerPrefetchWithNilNextIter— verifies no panic whennextIteris nilTestIterScannerPrefetchThresholdBoundary— verifies exact boundary positionTestIterScannerPrefetchOnlyOnce— verifiessync.Onceprevents duplicate fetches3 benchmarks:
BenchmarkIterScanner_Next— full Scanner path with nextIterBenchmarkIterScan— comparison: Iter.Scan() pathBenchmarkIterScanner_NextNoNextIter— Scanner path without nextIter