From c3fe485f91d2efdef09ef1bbc5334a27d27558c2 Mon Sep 17 00:00:00 2001 From: Imran Zaheer Date: Sat, 15 Aug 2026 11:52:16 +0500 Subject: [PATCH v2] Fix checkpointer restartpoint assertion failure. The checkpointer can try to create a restartpoint while WAL recovery is still in progress. During that work it truncates pg_subtrans when hot_standby is enabled, but that is only safe after recovery has started pg_subtrans. Some recovery paths, such as restoring a standalone hot backup without recovery.signal, can run recovery with EnableHotStandby set but without initializing hot standby's recovery transaction environment. In that case pg_subtrans has not been started when a restartpoint is attempted, leading to assertion failures while computing the transaction horizon for TruncateSUBTRANS(). Track in shared recovery state whether pg_subtrans has been initialized for hot standby during recovery, set it immediately after StartupSUBTRANS(), and have the checkpointer consult that flag before truncating pg_subtrans during restartpoints. --- src/backend/access/transam/xlog.c | 7 +++--- src/backend/access/transam/xlogrecovery.c | 26 +++++++++++++++++++++++ src/include/access/xlogrecovery.h | 8 +++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index c3baca5193b..b54806a16c3 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6234,6 +6234,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 @@ -8363,10 +8364,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. */ diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index 6de13b91748..38063b80470 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -4451,6 +4451,32 @@ SetPromoteIsTriggered(void) LocalPromoteIsTriggered = true; } +/* + * Has hot standby initialization started pg_subtrans? + */ +bool +RecoverySubtransInitialized(void) +{ + bool result; + + SpinLockAcquire(&XLogRecoveryCtl->info_lck); + result = XLogRecoveryCtl->SharedRecoverySubtransInitialized; + SpinLockRelease(&XLogRecoveryCtl->info_lck); + + return result; +} + +/* + * Remember that hot standby initialization has started pg_subtrans. + */ +void +SetRecoverySubtransInitialized(void) +{ + SpinLockAcquire(&XLogRecoveryCtl->info_lck); + XLogRecoveryCtl->SharedRecoverySubtransInitialized = true; + SpinLockRelease(&XLogRecoveryCtl->info_lck); +} + /* * Check whether a promote request has arrived. */ diff --git a/src/include/access/xlogrecovery.h b/src/include/access/xlogrecovery.h index a1d8a81dbc1..8786b6d3a8e 100644 --- a/src/include/access/xlogrecovery.h +++ b/src/include/access/xlogrecovery.h @@ -77,6 +77,12 @@ typedef struct XLogRecoveryCtlData */ bool SharedPromoteIsTriggered; + /* + * SharedRecoverySubtransInitialized indicates whether hot standby + * initialization has started pg_subtrans. Protected by info_lck. + */ + bool SharedRecoverySubtransInitialized; + /* * recoveryWakeupLatch is used to wake up the startup process to continue * WAL replay, if it is waiting for WAL to arrive or promotion to be @@ -220,6 +226,8 @@ extern XLogRecPtr GetCurrentReplayRecPtr(TimeLineID *replayEndTLI); extern bool PromoteIsTriggered(void); extern bool CheckPromoteSignal(void); +extern bool RecoverySubtransInitialized(void); +extern void SetRecoverySubtransInitialized(void); extern void WakeupRecovery(void); extern void StartupRequestWalReceiverRestart(void); -- 2.55.0