From 439f4af23264c180e3c3c0f217d57da469c5f46d Mon Sep 17 00:00:00 2001 From: Yuhang Qiu Date: Wed, 26 Aug 2026 11:57:21 +0000 Subject: [PATCH v3 1/4] read_stream: Allow changing the buffer access strategy Heap scans can replace their buffer access strategy during a rescan. A read stream retained across such a rescan would keep references to the old strategy, which may then be freed. Add read_stream_set_strategy() to update an idle stream. It changes the strategy used by subsequent reads and recomputes the pin limit within the existing allocation. Clamp current and saved look-ahead distances to the new limit. Reviewed-by: Nazir Bilal Yavuz Discussion: https://postgr.es/m/D1F99EB2-5A03-465F-A210-98F7DF256168@gmail.com --- src/backend/storage/aio/read_stream.c | 42 +++++++++++++++++++++++++++ src/include/storage/read_stream.h | 2 ++ 2 files changed, 44 insertions(+) diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.c index e7dbbe03326..6bc5b000fb1 100644 --- a/src/backend/storage/aio/read_stream.c +++ b/src/backend/storage/aio/read_stream.c @@ -1407,6 +1407,48 @@ read_stream_resume(ReadStream *stream) stream->combine_distance = stream->resume_combine_distance; } +/* + * Change the buffer access strategy used for reads from this stream, or pass + * NULL to stop using one. + * + * The stream must be idle. Buffers acquired under the old strategy are + * tracked in its ring, and in-progress IOs still refer to it, so callers that + * need to abandon a strategy on a busy stream use read_stream_clear_strategy() + * instead. + * + * The pin limit was derived from the strategy when the stream was created, so + * recompute it here. The single allocation made at that point cannot grow, so + * the new limit stays capped by what was allocated. + */ +void +read_stream_set_strategy(ReadStream *stream, BufferAccessStrategy strategy) +{ + Assert(stream->pinned_buffers == 0); + Assert(stream->ios_in_progress == 0); + Assert(stream->pending_read_nblocks == 0); + + for (int i = 0; i < stream->max_ios; ++i) + stream->ios[i].op.strategy = strategy; + + stream->max_pinned_buffers = Max(1, + Min(stream->queue_size - 1, + GetAccessStrategyPinLimit(strategy))); + + /* + * The look-ahead distances are only clamped as they grow, so a limit that + * just shrank has to be applied to them here, including the distances + * saved for resuming a paused stream. + */ + stream->readahead_distance = Min(stream->readahead_distance, + stream->max_pinned_buffers); + stream->combine_distance = Min(stream->combine_distance, + stream->max_pinned_buffers); + stream->resume_readahead_distance = Min(stream->resume_readahead_distance, + stream->max_pinned_buffers); + stream->resume_combine_distance = Min(stream->resume_combine_distance, + stream->max_pinned_buffers); +} + /* * Stop using a buffer access strategy for reads from this stream. * diff --git a/src/include/storage/read_stream.h b/src/include/storage/read_stream.h index e2dcf1be50a..feac23c9b51 100644 --- a/src/include/storage/read_stream.h +++ b/src/include/storage/read_stream.h @@ -102,6 +102,8 @@ extern ReadStream *read_stream_begin_smgr_relation(int flags, size_t per_buffer_data_size); extern BlockNumber read_stream_pause(ReadStream *stream); extern void read_stream_resume(ReadStream *stream); +extern void read_stream_set_strategy(ReadStream *stream, + BufferAccessStrategy strategy); extern void read_stream_clear_strategy(ReadStream *stream); extern void read_stream_reset(ReadStream *stream); extern void read_stream_end(ReadStream *stream); -- 2.43.7