postgres.git / summary / log / commit / refs

commit    d3c9ccdf1edec4f486a860e279d2ef2e22f5e4f1
Author:   Fujii Masao <fujii@postgresql.org>
Date:     Wed Sep 02 05:35:10 2026 +0000

    Fix checkpointer restartpoint assertion failure
    
    When recovery starts from a backup without a signal file, pg_subtrans
    is not started at the beginning of recovery and remains unstarted
    throughout recovery. However, previously, a restartpoint run by
    the checkpointer during recovery nevertheless tried to truncate
    pg_subtrans, triggering the assertion failure:
    
        TRAP: failed Assert("TransactionIdIsValid(initial)")
    
    This commit fixes this by tracking whether pg_subtrans has been
    started during recovery, and have the checkpointer check this flag
    before truncating pg_subtrans at restartpoints.
    
    Backpatch to all supported versions.
    
    Reported-by: Imran Zaheer <imran.zhir@gmail.com>
    Author: Imran Zaheer <imran.zhir@gmail.com>
    Author: Fujii Masao <masao.fujii@gmail.com>
    Reviewed-by: Michael Paquier <michael@paquier.xyz>
    Discussion: https://postgr.es/m/CA+UBfamzfEReT0VOGRUW_=_AecCYQ-CNKLR_T4Q+YEmJAH3fdg@mail.gmail.com
    Backpatch-through: 14


src/backend/access/transam/xlog.c | 42 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index ec84c77ddfa..bb84525e665 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -689,6 +689,12 @@ typedef struct XLogCtlData */ bool SharedPromoteIsTriggered; + /* + * SharedRecoverySubtransInitialized indicates whether hot standby + * initialization has started pg_subtrans. Protected by info_lck. + */ + bool SharedRecoverySubtransInitialized; + /* * WalWriterSleeping indicates whether the WAL writer is currently in * low-power mode (and hence should be nudged if an async commit occurs). @@ -971,6 +977,8 @@ static void ReadControlFile(void); static char *str_time(pg_time_t tnow); static void SetPromoteIsTriggered(void); static bool CheckForStandbyTrigger(void); +static bool RecoverySubtransInitialized(void); +static void SetRecoverySubtransInitialized(void); #ifdef WAL_DEBUG static void xlog_outrec(StringInfo buf, XLogReaderState *record); @@ -5295,6 +5303,7 @@ XLOGShmemInit(void) XLogCtl->SharedHotStandbyActive = false; XLogCtl->InstallXLogFileSegmentActive = false; XLogCtl->SharedPromoteIsTriggered = false; + XLogCtl->SharedRecoverySubtransInitialized = false; XLogCtl->WalWriterSleeping = false; SpinLockInit(&XLogCtl->Insert.insertpos_lck); @@ -7308,6 +7317,7 @@ StartupXLOG(void) * during recovery and need not be started yet. */ StartupSUBTRANS(oldestActiveXID); + SetRecoverySubtransInitialized(); /* * If we're beginning at a shutdown checkpoint, we know that @@ -10029,10 +10039,10 @@ CreateRestartPoint(int flags) * Truncate pg_subtrans if possible. We can throw away all data before * the oldest XMIN of any running transaction. No future transaction will * attempt to reference any pg_subtrans entry older than that (see Asserts - * in subtrans.c). When hot standby is disabled, though, we mustn't do - * this because StartupSUBTRANS hasn't been called yet. + * in subtrans.c). During recovery, don't truncate pg_subtrans until hot + * standby initialization has started it. */ - if (EnableHotStandby) + if (RecoverySubtransInitialized()) TruncateSUBTRANS(GetOldestTransactionIdConsideredRunning()); /* Real work is done; log and update stats. */ @@ -13408,6 +13418,32 @@ CheckPromoteSignal(void) return false; } +/* + * Has hot standby initialization started pg_subtrans? + */ +static bool +RecoverySubtransInitialized(void) +{ + bool result; + + SpinLockAcquire(&XLogCtl->info_lck); + result = XLogCtl->SharedRecoverySubtransInitialized; + SpinLockRelease(&XLogCtl->info_lck); + + return result; +} + +/* + * Remember that hot standby initialization has started pg_subtrans. + */ +static void +SetRecoverySubtransInitialized(void) +{ + SpinLockAcquire(&XLogCtl->info_lck); + XLogCtl->SharedRecoverySubtransInitialized = true; + SpinLockRelease(&XLogCtl->info_lck); +} + /* * Wake up startup process to replay newly arrived WAL, or to notice that * failover has been requested. [parent: 03851df1105d]