Re: index prefetching
Tomas Vondra <tomas@vondra.me>
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
aio: io_uring: Trigger async processing for large IOs
- a9ee66881744 19 (unreleased) landed
-
read stream: Split decision about look ahead for AIO and combining
- 8ca147d582a5 19 (unreleased) landed
-
read_stream: Only increase read-ahead distance when waiting for IO
- f63ca3379025 19 (unreleased) landed
-
read_stream: Prevent distance from decaying too quickly
- 6e36930f9aaf 19 (unreleased) landed
-
Reduce ExecSeqScan* code size using pg_assume()
- b227b0bb4e03 19 (unreleased) cited
-
Fix rare bug in read_stream.c's split IO handling.
- b421223172a2 19 (unreleased) cited
-
Fix multiranges to behave more like dependent types.
- 3e8235ba4f9c 17.0 cited
-
Add EXPLAIN (MEMORY) to report planner memory consumption
- 5de890e3610d 17.0 cited
-
Optimize nbtree backward scan boundary cases.
- c9c0589fda0e 17.0 cited
-
Increment xactCompletionCount during subtransaction abort.
- 90c885cdab8b 14.0 cited
-
Add nbtree Valgrind buffer lock checks.
- 4a70f829d86c 14.0 cited
-
Add nbtree high key "continuescan" optimization.
- 29b64d1de7c7 12.0 cited
-
Reduce pinning and buffer content locking for btree scans.
- 2ed5b87f96d4 9.5.0 cited
-
Teach btree to handle ScalarArrayOpExpr quals natively.
- 9e8da0f75731 9.2.0 cited
On 8/14/25 23:55, Peter Geoghegan wrote: > On Thu, Aug 14, 2025 at 5:06 PM Peter Geoghegan <pg@bowt.ie> wrote: >> If this same mechanism remembered (say) the last 2 heap blocks it >> requested, that might be enough to totally fix this particular >> problem. This isn't a serious proposal, but it'll be simple enough to >> implement. Hopefully when I do that (which I plan to soon) it'll fully >> validate your theory. > > I spoke too soon. It isn't going to be so easy, since > heapam_index_fetch_tuple wants to consume buffers as a simple stream. > There's no way that index_scan_stream_read_next can just suppress > duplicate block number requests (in a way that's more sophisticated > than the current trivial approach that stores the very last block > number in IndexScanBatchState.lastBlock) without it breaking the whole > concept of a stream of buffers. > I believe this idea (checking not just the very last block, but keeping a bit longer history) was briefly discussed a couple months ago, after you pointed out the need for the "last block" optimization (which the patch didn't have). At that point we were focused on addressing a regression with correlated indexes, so the single block was enough. But as you point out, it's harder than it seems. If I recall correctly, the challenge is that heapam_index_fetch_tuple() is expected to release the block when it changes, but then how would it know there's no future read of the same buffer in the stream? >>> We can optimize that by deferring the StartBufferIO() if we're encountering a >>> buffer that is undergoing IO, at the cost of some complexity. I'm not sure >>> real-world queries will often encounter the pattern of the same block being >>> read in by a read stream multiple times in close proximity sufficiently often >>> to make that worth it. >> >> We definitely need to be prepared for duplicate prefetch requests in >> the context of index scans. > > Can you (or anybody else) think of a quick and dirty way of working > around the problem on the read stream side? I would like to prioritize > getting the patch into a state where its overall performance profile > "feels right". From there we can iterate on fixing the underlying > issues in more principled ways. > > FWIW it wouldn't be that hard to require the callback (in our case > index_scan_stream_read_next) to explicitly point out that it knows > that the block number it's requesting has to be a duplicate. It might > make sense to at least place that much of the burden on the > callback/client side. > I don't recall all the details, but IIRC my impression was it'd be best to do this "caching" entirely in the read_stream.c (so the next_block callbacks would probably not need to worry about lastBlock at all), enabled when creating the stream. And then there would be something like read_stream_release_buffer() that'd do the right to release the buffer when it's not needed. regards -- Tomas Vondra