From 309e5f0fa1c43f40d1ab6a5579f8cb39e3d1a70f Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sat, 5 Sep 2026 23:22:33 +0000
Subject: [PATCH v7 2/3] Use WALReadFromBuffers() for local WAL reads.

Commit 91f2cae7a4 introduced WALReadFromBuffers() for physical
replication walsenders, and it has since been used for logical
replication walsenders too. This commit uses it for the remaining
callers that read WAL from the local server through the shared
page-read callback, namely logical decoding driven by SQL
functions, two-phase commit, repack workers, and pg_walinspect.

When the requested WAL is still in the WAL buffers, it can be
read from there instead of from a file. The gain is largest with
WAL direct I/O, where a file read is a physical disk read.
Without direct I/O it still saves a syscall and does not regress.
The benefit depends on the workload and how closely the caller
follows the insertion point.

The timeline passed to WALReadFromBuffers() is the one the read
targets, the same one passed to WALRead(), so that a read on a
historical timeline never comes from the WAL buffers.

As for the logical replication walsender, a read fully satisfied
from WAL buffers can leave the segment file open on the wrong
segment. Fix this by closing the open segment when such a read is
not in the open segment, so the next file read reopens the
correct one.

Author: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Jingtang Zhang <mrdrivingduck@gmail.com>
Reviewed-by: Nitin Jadhav <nitinjadhavpostgres@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://www.postgresql.org/message-id/CALj2ACVfF2Uj9NoFy-5m98HNtjHpuD17EDE9twVeJng-jTAe7A%40mail.gmail.com
---
 src/backend/access/transam/xlogutils.c | 29 +++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 58b9dab6a90..75c2ef37f95 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -900,6 +900,7 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
 	int			count;
 	WALReadError errinfo;
 	TimeLineID	currTLI;
+	Size		rbytes;
 
 	loc = targetPagePtr + reqLen;
 
@@ -1031,9 +1032,31 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
 		count = read_upto - targetPagePtr;
 	}
 
-	if (!WALRead(state, cur_page, targetPagePtr, count, tli,
-				 &errinfo))
-		WALReadRaiseError(&errinfo);
+	/* attempt to read WAL from WAL buffers first */
+	rbytes = WALReadFromBuffers(cur_page, targetPagePtr, count, tli);
+
+	/* now read the remaining WAL from WAL file */
+	if (rbytes < count)
+	{
+		if (!WALRead(state,
+					 cur_page + rbytes,
+					 targetPagePtr + rbytes,
+					 count - rbytes,
+					 tli,
+					 &errinfo))
+			WALReadRaiseError(&errinfo);
+	}
+	else if (state->seg.ws_file >= 0 &&
+			 !XLByteInSeg(targetPagePtr, state->seg.ws_segno,
+						  state->segcxt.ws_segsize))
+	{
+		/*
+		 * Close the segment when a read fully satisfied from WAL buffers is
+		 * not in the open segment, so the next file read reopens the correct
+		 * one. See logical_read_xlog_page() for why this is needed.
+		 */
+		state->routine.segment_close(state);
+	}
 
 	/* number of valid bytes in the buffer */
 	return count;
-- 
2.47.3

