From f641116324b9c6ca45f78d328e3c88d5b2400108 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Fri, 9 Oct 2026 04:16:25 +0000
Subject: [PATCH v6 1/2] Fix end LSN reported for an xlog-switch record ending
 on a page boundary

A page header size is added to the end of an xlog-switch record
even when the record ends exactly on a page boundary and crosses
no header. At a segment boundary that puts the end past the end of
reserved WAL. The position then goes into the asynchronous commit
LSN, and from there into the WAL writer, which asks to write and
flush WAL that was never generated. If the page at the boundary
has not been initialized yet, XLogWrite() then PANICs with "xlog
write request ... is past end of log". Otherwise it reports write
and flush positions for WAL that was never written, and in an
assert-enabled build fails an assertion there.

Adjust the end position to the start of the segment (before the
page header) using XLogBytePosToEndRecPtr(), the way it is done
for any other record. The bad position then no longer goes into
the asynchronous commit LSN or the WAL writer.

This also makes pg_switch_wal() return what its documentation says:
the ending location plus 1 within the just-completed WAL file.
---
 src/backend/access/transam/xlog.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 63d214782da..a9571ad8f61 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -1077,21 +1077,14 @@ XLogInsertRecord(XLogRecData *rdata,
 		/*
 		 * Even though we reserved the rest of the segment for us, which is
 		 * reflected in EndPos, we return a pointer to just the end of the
-		 * xlog-switch record.
+		 * xlog-switch record. When the record falls right on the segment
+		 * boundary, EndPos is adjusted to the start of the segment (before
+		 * the page header), so that it doesn't go past the end of reserved
+		 * WAL.
 		 */
 		if (inserted)
-		{
-			EndPos = StartPos + SizeOfXLogRecord;
-			if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
-			{
-				uint64		offset = XLogSegmentOffset(EndPos, wal_segment_size);
-
-				if (offset == EndPos % XLOG_BLCKSZ)
-					EndPos += SizeOfXLogLongPHD;
-				else
-					EndPos += SizeOfXLogShortPHD;
-			}
-		}
+			EndPos = XLogBytePosToEndRecPtr(XLogRecPtrToBytePos(StartPos) +
+											MAXALIGN(SizeOfXLogRecord));
 	}
 
 	/* Process any flush progress published while making room for the record. */
-- 
2.50.1 (Apple Git-155)

