postgres.git / summary / log / commit / refs
commit bdaa5a0e4a7e8d0769c7a88c93eb9188cf8cccac
Author: Fujii Masao <fujii@postgresql.org>
Date: Wed Sep 02 05:34:34 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 | 7 ++++---
src/backend/access/transam/xlogrecovery.c | 32 +++++++++++++++++++++++++++++++
src/include/access/xlogrecovery.h | 2 ++
3 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f79961633a6..e9508fe9d7b 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5260,6 +5260,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
@@ -7243,10 +7244,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 b07a54a9216..26738af34ba 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -313,6 +313,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 failover trigger file
@@ -4492,6 +4498,32 @@ CheckPromoteSignal(void)
return false;
}
+/*
+ * 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);
+}
+
/*
* Wake up startup process to replay newly arrived WAL, or to notice that
* failover has been requested.
diff --git a/src/include/access/xlogrecovery.h b/src/include/access/xlogrecovery.h
index 0aa85d90e89..55ccbe23f0b 100644
--- a/src/include/access/xlogrecovery.h
+++ b/src/include/access/xlogrecovery.h
@@ -145,6 +145,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);
[parent: b6d2d82871c3]