From f20b8a07a59cc704e7636ce6b76415cfa49129c2 Mon Sep 17 00:00:00 2001 From: Xuneng Zhou Date: Wed, 9 Sep 2026 11:01:13 +0800 Subject: [PATCH v3 3/3] Re-read standby LSN after recovery ends WaitForLSN() samples the current position before checking whether recovery is still in progress. If recovery reaches the target and ends between those operations, the promotion path can compare against the stale sample and incorrectly return NOT_IN_RECOVERY. Read the position again after observing that recovery has ended, before deciding whether promotion reached the target. Author: Xuneng Zhou Discussion: https://postgr.es/m/CABPTF7U0gW5%2B-4oL7-qdML-yerZxUb7ku4QXp7JxCYo0qyJ_Tw%40mail.gmail.com Reviewed-by: Alexander Korotkov Backpatch-through: 19 --- src/backend/access/transam/xlogwait.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/backend/access/transam/xlogwait.c b/src/backend/access/transam/xlogwait.c index c624f9163a1..fd3cac9103d 100644 --- a/src/backend/access/transam/xlogwait.c +++ b/src/backend/access/transam/xlogwait.c @@ -488,11 +488,19 @@ WaitForLSN(WaitLSNType lsnType, XLogRecPtr targetLSN, int64 timeout) if (WaitLSNTypeRequiresRecovery(lsnType) && !RecoveryInProgress()) { /* - * Recovery was ended, but check if target LSN was already + * Recovery has ended, but check if target LSN was already * reached. */ deleteLSNWaiter(lsnType); + /* + * Recovery may have advanced the current position after currentLSN + * was read above. Once RecoveryInProgress() returns false, the final + * position is stable, so read it again before deciding whether + * promotion reached the target. + */ + currentLSN = GetCurrentLSNForWaitType(lsnType); + if (PromoteIsTriggered() && targetLSN <= currentLSN) return WAIT_LSN_RESULT_SUCCESS; return WAIT_LSN_RESULT_NOT_IN_RECOVERY; -- 2.50.1 (Apple Git-155)