From ba79f259177b137b969660f0311e7bef37efc1cf Mon Sep 17 00:00:00 2001
From: Satya Narlapuram <satyanarlapuram@gmail.com>
Date: Wed, 15 Apr 2026 06:24:35 +0000
Subject: [PATCH] Fix WAIT FOR LSN standby_write/standby_flush for archive
 recovery

GetCurrentLSNForWaitType() for WAIT_LSN_TYPE_STANDBY_WRITE and
WAIT_LSN_TYPE_STANDBY_FLUSH previously relied solely on the WAL receiver's
tracked write/flush positions (GetWalRcvWriteRecPtr/GetWalRcvFlushRecPtr).

In case of Archive recovery, no WAL receiver process exists, so these
functions return InvalidXLogRecPtr (0/0). WAIT FOR LSN with standby_flush or
standby_write modes would always time out, even for WAL that has been
fully replayed.

Fix by falling back to the replay LSN (GetXLogReplayRecPtr) when the WAL
receiver position is invalid or behind replay. This is correct because any
WAL that has been replayed has necessarily already been written and flushed
to disk.
---
 src/backend/access/transam/xlogwait.c | 30 ++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/transam/xlogwait.c b/src/backend/access/transam/xlogwait.c
index 2e31c0d6..d7844e8d 100644
--- a/src/backend/access/transam/xlogwait.c
+++ b/src/backend/access/transam/xlogwait.c
@@ -93,6 +93,13 @@ StaticAssertDecl(lengthof(WaitLSNWaitEvents) == WAIT_LSN_TYPE_COUNT,
 
 /*
  * Get the current LSN for the specified wait type.
+ *
+ * For standby write and flush types, we first consult the WAL receiver's
+ * tracked position. However, during archive recovery (or after pg_rewind
+ * when the WAL receiver hasn't caught up), the WAL receiver may report 0/0
+ * or a position behind the replay LSN. In those cases, we fall back to the
+ * replay LSN, because any WAL that has been replayed has necessarily already
+ * been written and flushed to disk.
  */
 XLogRecPtr
 GetCurrentLSNForWaitType(WaitLSNType lsnType)
@@ -105,10 +112,27 @@ GetCurrentLSNForWaitType(WaitLSNType lsnType)
 			return GetXLogReplayRecPtr(NULL);
 
 		case WAIT_LSN_TYPE_STANDBY_WRITE:
-			return GetWalRcvWriteRecPtr();
-
 		case WAIT_LSN_TYPE_STANDBY_FLUSH:
-			return GetWalRcvFlushRecPtr(NULL, NULL);
+			{
+				XLogRecPtr	walrcvPtr;
+				XLogRecPtr	replayPtr;
+
+				if (lsnType == WAIT_LSN_TYPE_STANDBY_WRITE)
+					walrcvPtr = GetWalRcvWriteRecPtr();
+				else
+					walrcvPtr = GetWalRcvFlushRecPtr(NULL, NULL);
+
+				replayPtr = GetXLogReplayRecPtr(NULL);
+
+				/*
+				 * Use the WAL receiver position if it is valid and ahead of
+				 * the replay position. Otherwise, fall back to the replay
+				 * LSN. This handles archive recovery (no WAL receiver).
+				 */
+				if (walrcvPtr != InvalidXLogRecPtr && walrcvPtr > replayPtr)
+					return walrcvPtr;
+				return replayPtr;
+			}
 
 		case WAIT_LSN_TYPE_PRIMARY_FLUSH:
 			return GetFlushRecPtr(NULL);
-- 
2.43.0

