1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

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
This commit is contained in:
Fujii Masao
2026-09-02 14:35:10 +09:00
parent 03851df110
commit d3c9ccdf1e

View File

@@ -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.