From fb430f93d7779755d97149e4d549a286778bd5a4 Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Sun, 13 Sep 2026 23:44:30 +0000 Subject: [PATCH v9 3/5] 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 that comes entirely from the 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 that the next file read reopens the correct one. One of these callers runs in the checkpointer, which reads the WAL of a prepared transaction while writing its two-phase state file. So pg_stat_io now reports hits on object wal for the checkpointer as well. Author: Bharath Rupireddy Reviewed-by: Jingtang Zhang Reviewed-by: Nitin Jadhav Reviewed-by: Michael Paquier Reviewed-by: Kirill Reshke 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..9007642dd22 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 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); + } /* number of valid bytes in the buffer */ return count; -- 2.47.3