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