postgres.git / summary / log / commit / refs

commit    6bc236c8a5b198e7ffde80377777167f1d4b78bb
Author:   Alexander Korotkov <akorotkov@postgresql.org>
Date:     Sat Sep 12 20:30:03 2026 +0000

    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 <xunengzhou@gmail.com>
    Discussion: https://postgr.es/m/CABPTF7U0gW5%2B-4oL7-qdML-yerZxUb7ku4QXp7JxCYo0qyJ_Tw%40mail.gmail.com
    Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
    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..4fd11e66284 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; [parent: 854be98cc4aa]