v1-0002-Fix-WAIT-FOR-LSN-standby_write-standby_flush-hang.patch

application/octet-stream

Filename: v1-0002-Fix-WAIT-FOR-LSN-standby_write-standby_flush-hang.patch
Type: application/octet-stream
Part: 1
Message: Re: Implement waiting for wal lsn replay: reloaded

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v1-0002
Subject: Fix WAIT FOR LSN standby_write/standby_flush hang on idle primary
File+
src/backend/access/transam/xlogwait.c 22 2
src/backend/replication/walreceiver.c 11 0
From e542061b02f154c4c81a989b239a71b02dc09638 Mon Sep 17 00:00:00 2001
From: alterego655 <824662526@qq.com>
Date: Tue, 7 Apr 2026 20:18:26 +0800
Subject: [PATCH v1 2/2] Fix WAIT FOR LSN standby_write/standby_flush hang on
 idle primary

After walreceiver connects, WalRcv->writtenUpto remains at 0 until
new streaming data arrives.  If the target LSN is already on disk
from a base backup or archive restore and the primary is idle, WAIT
FOR LSN with standby_write or standby_flush mode blocks indefinitely.

Fix by having GetCurrentLSNForWaitType() fall back to the replay
position when the walreceiver hasn't written any WAL yet.  WAL up
to the replay point is already on disk and should satisfy the wait.
Additionally, wake any already-blocked waiters at walreceiver
startup so they re-check their condition through the new fallback.

Reported-by: Tom Lane
---
 src/backend/access/transam/xlogwait.c | 24 ++++++++++++++++++++++--
 src/backend/replication/walreceiver.c | 11 +++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/transam/xlogwait.c b/src/backend/access/transam/xlogwait.c
index 2e31c0d67d7..7b102a16fc4 100644
--- a/src/backend/access/transam/xlogwait.c
+++ b/src/backend/access/transam/xlogwait.c
@@ -105,10 +105,30 @@ GetCurrentLSNForWaitType(WaitLSNType lsnType)
 			return GetXLogReplayRecPtr(NULL);
 
 		case WAIT_LSN_TYPE_STANDBY_WRITE:
-			return GetWalRcvWriteRecPtr();
+			{
+				XLogRecPtr	recptr = GetWalRcvWriteRecPtr();
+
+				/*
+				 * If the walreceiver hasn't written any WAL yet, fall back to
+				 * the replay position.  WAL up to the replay point is already
+				 * on disk (from base backup, archive restore, or prior
+				 * streaming), so there is no reason to wait for the
+				 * walreceiver to re-receive it.
+				 */
+				if (recptr == InvalidXLogRecPtr)
+					recptr = GetXLogReplayRecPtr(NULL);
+				return recptr;
+			}
 
 		case WAIT_LSN_TYPE_STANDBY_FLUSH:
-			return GetWalRcvFlushRecPtr(NULL, NULL);
+			{
+				XLogRecPtr	recptr = GetWalRcvFlushRecPtr(NULL, NULL);
+
+				/* Same fallback as standby_write; see comment above. */
+				if (recptr == InvalidXLogRecPtr)
+					recptr = GetXLogReplayRecPtr(NULL);
+				return recptr;
+			}
 
 		case WAIT_LSN_TYPE_PRIMARY_FLUSH:
 			return GetFlushRecPtr(NULL);
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index c7dcb3003b5..0cb734cd2fc 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -411,6 +411,17 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 			LogstreamResult.Write = LogstreamResult.Flush = GetXLogReplayRecPtr(NULL);
 			initStringInfo(&reply_message);
 
+			/*
+			 * Wake any already-blocked standby_write or standby_flush
+			 * waiters. Their target LSN may already be satisfied by WAL on
+			 * disk from a base backup or archive restore. We don't seed the
+			 * shared-memory positions here; instead,
+			 * GetCurrentLSNForWaitType() falls back to the replay position
+			 * when the walreceiver hasn't written anything yet.
+			 */
+			WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_WRITE, LogstreamResult.Write);
+			WaitLSNWakeup(WAIT_LSN_TYPE_STANDBY_FLUSH, LogstreamResult.Flush);
+
 			/* Initialize nap wakeup times. */
 			now = GetCurrentTimestamp();
 			for (int i = 0; i < NUM_WALRCV_WAKEUPS; ++i)
-- 
2.51.0