From 7c32867976d485beb3580f0347b824dbf021aa0c Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Mon, 10 Mar 2025 18:17:06 -0400
Subject: [PATCH v35 2/5] Add READ_STREAM_RANDOM flag

For read stream users with random I/O that want to explicitly force
prefetching. The read stream API attempts to detect sequential access
and shuts off prefetching. Some users may have runs of sequential blocks
but wish to issue advice anyway. Provide this flag for this purpose.

This commit adds no users of this flag.
---
 src/backend/storage/aio/read_stream.c | 38 ++++++++++++++++++++++-----
 src/include/storage/read_stream.h     |  7 +++++
 src/tools/pgindent/typedefs.list      |  1 +
 3 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.c
index 04bdb5e6d4b..b4dea112d9a 100644
--- a/src/backend/storage/aio/read_stream.c
+++ b/src/backend/storage/aio/read_stream.c
@@ -103,6 +103,19 @@ typedef struct InProgressIO
 	ReadBuffersOperation op;
 } InProgressIO;
 
+/*
+ * Whether or not the read stream should attempt to issue advice.
+ * RS_ADVICE_NEVER disables prefetching. RS_ADVICE_ADAPTIVE enables it in
+ * general but may temporarily disable it if sequential access is detected.
+ * RS_ADVICE_ALWAYS will always issue prefetches.
+ */
+typedef enum ReadStreamAdviceLevel
+{
+	RS_ADVICE_NEVER,
+	RS_ADVICE_ADAPTIVE,
+	RS_ADVICE_ALWAYS,
+} ReadStreamAdviceLevel;
+
 /*
  * State for managing a stream of reads.
  */
@@ -114,7 +127,7 @@ struct ReadStream
 	int16		max_pinned_buffers;
 	int16		pinned_buffers;
 	int16		distance;
-	bool		advice_enabled;
+	ReadStreamAdviceLevel advice_level;
 
 	/*
 	 * One-block buffer to support 'ungetting' a block number, to resolve flow
@@ -228,6 +241,7 @@ static void
 read_stream_start_pending_read(ReadStream *stream, bool suppress_advice)
 {
 	bool		need_wait;
+	bool		sequence_detected;
 	int			nblocks;
 	int			flags;
 	int16		io_index;
@@ -248,13 +262,18 @@ read_stream_start_pending_read(ReadStream *stream, bool suppress_advice)
 	else
 		Assert(stream->next_buffer_index == stream->oldest_buffer_index);
 
+	sequence_detected = stream->pending_read_blocknum == stream->seq_blocknum;
+
 	/*
 	 * If advice hasn't been suppressed, this system supports it, and this
 	 * isn't a strictly sequential pattern, then we'll issue advice.
 	 */
-	if (!suppress_advice &&
-		stream->advice_enabled &&
-		stream->pending_read_blocknum != stream->seq_blocknum)
+	if (suppress_advice)
+		flags = 0;
+	else if (stream->advice_level == RS_ADVICE_ALWAYS)
+		flags = READ_BUFFERS_ISSUE_ADVICE;
+	else if (stream->advice_level == RS_ADVICE_ADAPTIVE &&
+			 !sequence_detected)
 		flags = READ_BUFFERS_ISSUE_ADVICE;
 	else
 		flags = 0;
@@ -501,6 +520,8 @@ read_stream_begin_impl(int flags,
 		stream->per_buffer_data = (void *)
 			MAXALIGN(&stream->ios[Max(1, max_ios)]);
 
+	stream->advice_level = RS_ADVICE_NEVER;
+
 #ifdef USE_PREFETCH
 
 	/*
@@ -512,7 +533,12 @@ read_stream_begin_impl(int flags,
 	if ((io_direct_flags & IO_DIRECT_DATA) == 0 &&
 		(flags & READ_STREAM_SEQUENTIAL) == 0 &&
 		max_ios > 0)
-		stream->advice_enabled = true;
+	{
+		if ((flags & READ_STREAM_RANDOM) != 0)
+			stream->advice_level = RS_ADVICE_ALWAYS;
+		else
+			stream->advice_level = RS_ADVICE_ADAPTIVE;
+	}
 #endif
 
 	/*
@@ -662,7 +688,7 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
 			if (likely(!StartReadBuffer(&stream->ios[0].op,
 										&stream->buffers[oldest_buffer_index],
 										next_blocknum,
-										stream->advice_enabled ?
+										stream->advice_level > RS_ADVICE_NEVER ?
 										READ_BUFFERS_ISSUE_ADVICE : 0)))
 			{
 				/* Fast return. */
diff --git a/src/include/storage/read_stream.h b/src/include/storage/read_stream.h
index c11d8ce3300..463dd4ac6df 100644
--- a/src/include/storage/read_stream.h
+++ b/src/include/storage/read_stream.h
@@ -42,6 +42,13 @@
  */
 #define READ_STREAM_FULL 0x04
 
+/*
+ * This flag explicitly disables sequential detection for cases in which we
+ * know we want to issue prefetch advice even though there may be some
+ * sequential runs of blocks.
+ */
+#define READ_STREAM_RANDOM 0x08
+
 struct ReadStream;
 typedef struct ReadStream ReadStream;
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9840060997f..b5b547f9e89 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2378,6 +2378,7 @@ ReadFunc
 ReadLocalXLogPageNoWaitPrivate
 ReadReplicationSlotCmd
 ReadStream
+ReadStreamAdviceLevel
 ReadStreamBlockNumberCB
 ReassignOwnedStmt
 RecheckForeignScan_function
-- 
2.34.1

