From 2dbc19b2e4871e636106be30a02e1753d22a0c2a Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Sat, 10 Oct 2026 19:10:50 +0000
Subject: [PATCH v10 4/4] Use WALReadFromBuffers() for WAL summarizer.

Commit 91f2cae7a4 introduced WALReadFromBuffers() and the
previous commits used it for physical and logical walsenders and
for the local WAL readers. This commit lets the WAL summarizer
also read WAL from the WAL buffers instead of always going to a
file.

This reduces read IO and syscalls, especially when the summarizer
keeps up with WAL generation. It helps even more with WAL direct
IO enabled, where a file read is always a physical disk read
without the OS page cache.

Author: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Discussion: https://postgr.es/m/CALj2ACVfF2Uj9NoFy-5m98HNtjHpuD17EDE9twVeJng-jTAe7A%40mail.gmail.com
---
 src/backend/postmaster/walsummarizer.c | 30 +++++++++++++++++++++++---
 src/bin/pg_walsummary/t/002_blocks.pl  |  5 +++--
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index 8333a4eaaa3..00a48f4c952 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -1659,6 +1659,7 @@ summarizer_read_local_xlog_page(XLogReaderState *state,
 	int			count;
 	WALReadError errinfo;
 	SummarizerReadLocalXLogPrivate *private_data;
+	Size		rbytes;
 
 	ProcessWalSummarizerInterrupts();
 
@@ -1750,9 +1751,32 @@ summarizer_read_local_xlog_page(XLogReaderState *state,
 		}
 	}
 
-	if (!WALRead(state, cur_page, targetPagePtr, count,
-				 private_data->tli, &errinfo))
-		WALReadRaiseError(&errinfo);
+	/* attempt to read WAL from WAL buffers first */
+	rbytes = WALReadFromBuffers(cur_page, targetPagePtr, count,
+								private_data->tli);
+
+	/* now read the remaining WAL from WAL file */
+	if (rbytes < count)
+	{
+		if (!WALRead(state,
+					 cur_page + rbytes,
+					 targetPagePtr + rbytes,
+					 count - rbytes,
+					 private_data->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 that comes entirely from the WAL
+		 * buffers is not in the open segment, so that the next file read
+		 * reopens the correct one.
+		 */
+		state->routine.segment_close(state);
+	}
 
 	/* Track that we read a page, for sleep time calculation. */
 	++pages_read_since_last_sleep;
diff --git a/src/bin/pg_walsummary/t/002_blocks.pl b/src/bin/pg_walsummary/t/002_blocks.pl
index c5a9649d6ad..ca0bf0002fc 100644
--- a/src/bin/pg_walsummary/t/002_blocks.pl
+++ b/src/bin/pg_walsummary/t/002_blocks.pl
@@ -58,10 +58,11 @@ command_like(
 command_like([ 'pg_walsummary', '-q', @summaries ],
 	qr/\A\z/, "-q suppresses all output");
 
-# The WAL summarizer should have generated some IO statistics.
+# The WAL summarizer should have generated some IO statistics (WAL reads from
+# buffers and file).
 $node1->poll_query_until(
 	'postgres',
-	q{SELECT sum(reads) > 0 FROM pg_stat_io
+	q{SELECT sum(hits) + sum(reads) > 0 FROM pg_stat_io
    WHERE backend_type = 'walsummarizer' AND object = 'wal'})
   or die
   "Timed out while waiting for WAL summarizer to generate statistics for WAL reads";
-- 
2.47.3

