Re: index prefetching
Peter Geoghegan <pg@bowt.ie>
Attachments
- v10-0011-Make-hash-index-AM-use-amgetbatch-interface.patch (application/x-patch) patch v10-0011
- v10-0008-Make-buffer-hit-helper.patch (application/x-patch) patch v10-0008
- v10-0001-Extract-fake-LSN-infrastructure-from-GiST-index-.patch (application/x-patch) patch v10-0001
- v10-0009-Don-t-wait-for-already-in-progress-IO.patch (application/x-patch) patch v10-0009
- v10-0010-Add-fake-LSN-support-to-hash-index-AM.patch (application/x-patch) patch v10-0010
- v10-0005-Add-prefetching-to-index-scans-using-batch-inter.patch (application/x-patch) patch v10-0005
- v10-0007-Limit-get_actual_variable_range-to-scan-one-inde.patch (application/x-patch) patch v10-0007
- v10-0004-Introduce-read_stream_-pause-resume-yield.patch (application/x-patch) patch v10-0004
- v10-0006-Use-ExecSetTupleBound-hint-during-index-scans.patch (application/x-patch) patch v10-0006
- v10-0003-Add-batching-interfaces-used-by-heapam-and-nbtre.patch (application/x-patch) patch v10-0003
- v10-0002-Use-fake-LSNs-to-improve-nbtree-dropPin-behavior.patch (application/x-patch) patch v10-0002
On Fri, Jan 30, 2026 at 7:18 PM Peter Geoghegan <pg@bowt.ie> wrote: > Attached is v9. Attached is v10. There are 2 major areas of improvement in this latest revision: 1. We have enhanced the read stream callback (heapam_getnext_stream, which is added by v10-0005-Add-prefetching-to-index-scans-using-batch-inter.patch), making it yield at key intervals. When we yield, we temporarily suspend prefetching -- but only for long enough to give the scan the opportunity to return one more matching tuple (don't confuse yielding with pausing; we do both, but the goals are rather different in each case). Yielding like this keeps the scan responsive during prefetching: the scan should never go too long without returning at least one tuple (except when that just isn't possible at all). Testing has shown that keeping the scan responsive in this sense is particularly important during scans that appear in "ORDER BY ... LIMIT N" queries, as well as during scans that feed into certain merge joins. IOW, it is particularly important that we "keep the scan responsive" whenever it has the potential to allow the scan to shut down before it has performed work that turns out to be unnecessary (though it also seems to have some benefits even when that isn't the case). There is a complex trade-off here: we want to yield when we expect some benefit from doing so. But we don't want to yield when doing so risks compromising the read stream's ability to maintain an adequate prefetch distance. There are comments in heapam_getnext_stream that describe the theory in more detail. There are heuristics that were derived using adversarial benchmarking/stress-testing. I'm sure that they need more work, but this does seem like roughly the right idea. Note that we now test whether the scan's read stream is using its fast-path mode (read stream uses this when the scan reads heap pages that are all cached). 2. A new patch (v10-0007-Limit-get_actual_variable_range-to-scan-one-inde.patch) compensates for the fact that the main prefetching commit removes get_actual_variable_range's VISITED_PAGES_LIMIT mechanism. Since VISITED_PAGES_LIMIT cannot easily be ported over to the new table AM interface selfuncs.c now uses. I described the problem that we need to address when I posted v9: > selfuncs.c problem > ------------------ > > Also worth noting that we recently found a problem with selfuncs.c: > the VISITED_PAGES_LIMIT stuff in selfuncs.c is broken right now. v9 > tears that code out, pending adding back a real fix (earlier versions > of the patch had VISITED_PAGES_LIMIT, but it didn't work). > > The underlying problem is that the existing VISITED_PAGES_LIMIT design > is incompatible with our new table_index_getnext_slot interface. The > new interface doesn't stop scanning until it is able to at least > return 1 tuple. But VISITED_PAGES_LIMIT was invented precisely because > get_actual_variable_endpoint's index scans took far too long, even > though they're only ever required to locate 1 tuple. So that just > won't work. > > We'll need to invent some kind of API that directly acknowledges the > needs of the selfuncs.c caller, and others like it. Doing it in an > ad-hoc way just doesn't seem possible anymore. That will have to wait > for the next revision, though (or the one after that). The new patch deals with the problem in a completely different way, and at a completely different layer: it adds a new IndexScanDescData.xs_read_extremal_only field, set only by get_actual_variable_range. When nbtree sees that the field has been set, it gives up after scanning only one leaf page (the page that contains extremal values that are of interest to get_actual_variable_range). Note that we completely stop caring about heap page fetches with this new approach. There are good reasons to believe that the new IndexScanDescData.xs_read_extremal_only approach will solve existing problems with VISITED_PAGES_LIMIT. Recent benchmarking from Mark Callaghan [1] (which I've independently recreated with my own minimal test suite) shows that VISITED_PAGES_LIMIT becomes completely ineffective, once we reach a tipping point where many index tuples at one end of the index all have their LP_DEAD bit set. I'm going to start a new thread to discuss the issues in this area later today. I'm aiming to fix an existing, independent issue in this new patch, so it makes sense to discuss it on a completely separate thread. [1] https://smalldatum.blogspot.com/2026/01/cpu-bound-insert-benchmark-vs-postgres.html -- Peter Geoghegan
Commits
-
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
-
aio: io_uring: Trigger async processing for large IOs
- a9ee66881744 19 (unreleased) landed
-
heapam: Keep buffer pins across index scan resets.
- 2d3490dd99f0 19 (unreleased) landed
-
heapam: Track heap block in IndexFetchHeapData.
- c7d09595e46f 19 (unreleased) landed
-
Move heapam_handler.c index scan code to new file.
- a29fdd6c8d81 19 (unreleased) landed
-
Rename heapam_index_fetch_tuple argument for clarity.
- 1adff1a0c558 19 (unreleased) landed
-
Optimize fast-path FK checks with batched index probes
- b7b27eb41a5c 19 (unreleased) cited
-
read_stream: Prevent distance from decaying too quickly
- 6e36930f9aaf 19 (unreleased) landed
-
read_stream: Issue IO synchronously while in fast path
- cceb1bf45e3a 19 (unreleased) landed
-
bufmgr: Return whether WaitReadBuffers() needed to wait
- 513374a47a71 19 (unreleased) landed
-
aio: io_uring: Allow IO methods to check if IO completed in the background
- 6e648e353fa0 19 (unreleased) landed
-
bufmgr: Make UnlockReleaseBuffer() more efficient
- f39cb8c01106 19 (unreleased) cited
-
Add fake LSN support to hash index AM.
- e5836f7b7d9a 19 (unreleased) landed
-
Make IndexScanInstrumentation a pointer in executor scan nodes.
- f026fbf059f2 19 (unreleased) landed
-
Use fake LSNs to improve nbtree dropPin behavior.
- 8a879119a1d1 19 (unreleased) landed
-
Move fake LSN infrastructure out of GiST.
- d774072f0040 19 (unreleased) landed
-
Use simplehash for backend-private buffer pin refcounts.
- a367c433ad01 19 (unreleased) landed
-
nbtree: Avoid allocating _bt_search stack.
- d071e1cfec23 19 (unreleased) landed
-
bufmgr: Fix use of wrong variable in GetPrivateRefCountEntrySlow()
- 6322a028fa43 19 (unreleased) landed
-
Conditional locking in pgaio_worker_submit_internal
- 29a0fb215779 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
-
Remove HeapBitmapScan's skip_fetch optimization
- 459e7bf8e2f8 18.0 cited
-
Optimize nbtree backwards scans.
- 1bd4bc85cac2 18.0 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