From c213cae3ded6467a7b56715105263ed49b2ae706 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Sat, 28 Jun 2025 18:53:29 +0530
Subject: [PATCH v3] Fix infinite wait when reading partially written WAL
 record after crash

If a crash occurs while writing a WAL record that spans multiple pages,
the recovery process marks the page with the XLP_FIRST_IS_OVERWRITE_CONTRECORD
flag. However, logical decoding currently attempts to read the full WAL record
based on its expected size before checking this flag, which can lead to an
infinite wait if the remaining data is never written (e.g., no activity after crash).

This patch updates the logic to first read the page header and check for the
XLP_FIRST_IS_OVERWRITE_CONTRECORD flag before attempting to reconstruct the
full WAL record. If the flag is set, decoding correctly identifies the record
as incomplete and avoids waiting for WAL data that will never arrive.
---
 src/backend/access/transam/xlogreader.c | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index a07f55dfbc9..d3dcccf4949 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -722,11 +722,14 @@ restart:
 			/* Calculate pointer to beginning of next page */
 			targetPagePtr += XLOG_BLCKSZ;
 
-			/* Wait for the next page to become available */
-			readOff = ReadPageInternal(state, targetPagePtr,
-									   Min(total_len - gotlen + SizeOfXLogShortPHD,
-										   XLOG_BLCKSZ));
-
+			/*
+			 * Read the page header before processing the WAL record data.
+			 * This is necessary to correctly handle cases where the previous
+			 * record ended as a partial record. Attempting to read the full
+			 * record without checking the header may result in waiting
+			 * indefinitely for data that doesn't exist.
+			 */
+			readOff = ReadPageInternal(state, targetPagePtr, SizeOfXLogShortPHD);
 			if (readOff == XLREAD_WOULDBLOCK)
 				return XLREAD_WOULDBLOCK;
 			else if (readOff < 0)
@@ -775,6 +778,15 @@ restart:
 				goto err;
 			}
 
+			/* Wait for the next page to become available */
+			readOff = ReadPageInternal(state, targetPagePtr,
+									   Min(total_len - gotlen + SizeOfXLogShortPHD,
+										   XLOG_BLCKSZ));
+			if (readOff == XLREAD_WOULDBLOCK)
+				return XLREAD_WOULDBLOCK;
+			else if (readOff < 0)
+				goto err;
+
 			/* Append the continuation from this page to the buffer */
 			pageHeaderSize = XLogPageHeaderSize(pageHeader);
 
-- 
2.43.0

