diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index 3a6c391..363db0e 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -479,6 +479,16 @@ ZeroCLOGPage(int pageno, bool writeXlog) void StartupCLOG(void) { + /* Currently this is a no-op */ + return; +} + +/* + * This must be called ONCE at the end of startup/recovery. + */ +void +TrimCLOG(void) +{ TransactionId xid = ShmemVariableCache->nextXid; int pageno = TransactionIdToPage(xid); diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index befb507..1535896 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6917,6 +6917,11 @@ StartupXLOG(void) StartupMultiXact(); } + /* + * Trim commit log to the nextxid, so that any later xid states are reset. + */ + TrimCLOG(); + /* Reload shared-memory state for prepared transactions */ RecoverPreparedTransactions(); @@ -7623,6 +7628,16 @@ CreateCheckPoint(int flags) checkPoint.time = (pg_time_t) time(NULL); /* + * For Hot Standby, derive the oldestActiveXid before we fix the redo pointer. + * This allows us to begin accumulating changes to assemble our starting + * snapshot of locks and transactions. + */ + if (!shutdown && XLogStandbyInfoActive()) + checkPoint.oldestActiveXid = GetOldestActiveTransactionId(); + else + checkPoint.oldestActiveXid = InvalidTransactionId; + + /* * We must hold WALInsertLock while examining insert state to determine * the checkpoint REDO pointer. */ @@ -7808,9 +7823,7 @@ CreateCheckPoint(int flags) * Update checkPoint.nextXid since we have a later value */ if (!shutdown && XLogStandbyInfoActive()) - LogStandbySnapshot(&checkPoint.oldestActiveXid, &checkPoint.nextXid); - else - checkPoint.oldestActiveXid = InvalidTransactionId; + LogStandbySnapshot(&checkPoint.nextXid); START_CRIT_SECTION(); diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 9489012..bbc5f44 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -1504,6 +1504,63 @@ GetRunningTransactionData(void) } /* + * GetOldestActiveTransactionId() + * + * Similar to GetSnapshotData but returns just oldestActiveXid. We include + * all PGPROCs with an assigned TransactionId, even VACUUM processes. + * We look at all databases, though there is no need to include WALSender + * since this has no effect on hot standby conflicts. + * + * This is never executed during recovery so there is no need to look at + * KnownAssignedXids. + * + * We don't worry about updating other counters, we want to keep this as + * simple as possible and leave GetSnapshotData() as the primary code for + * that bookkeeping. + */ +TransactionId +GetOldestActiveTransactionId(void) +{ + ProcArrayStruct *arrayP = procArray; + TransactionId oldestRunningXid; + int index; + + Assert(!RecoveryInProgress()); + + LWLockAcquire(ProcArrayLock, LW_SHARED); + + oldestRunningXid = ShmemVariableCache->nextXid; + + /* + * Spin over procArray collecting all xids and subxids. + */ + for (index = 0; index < arrayP->numProcs; index++) + { + volatile PGPROC *proc = arrayP->procs[index]; + TransactionId xid; + + /* Fetch xid just once - see GetNewTransactionId */ + xid = proc->xid; + + if (!TransactionIdIsNormal(xid)) + continue; + + if (TransactionIdPrecedes(xid, oldestRunningXid)) + oldestRunningXid = xid; + + /* + * Top-level XID of a transaction is always less than any of its + * subxids, so we don't need to check if any of the subxids are + * smaller than oldestRunningXid + */ + } + + LWLockRelease(ProcArrayLock); + + return oldestRunningXid; +} + +/* * GetTransactionsInCommit -- Get the XIDs of transactions that are committing * * Constructs an array of XIDs of transactions that are currently in commit diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 72c6b97..3f2fc81 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -815,7 +815,7 @@ standby_desc(StringInfo buf, uint8 xl_info, char *rec) * making WAL entries. */ void -LogStandbySnapshot(TransactionId *oldestActiveXid, TransactionId *nextXid) +LogStandbySnapshot(TransactionId *nextXid) { RunningTransactions running; xl_standby_lock *locks; @@ -845,7 +845,6 @@ LogStandbySnapshot(TransactionId *oldestActiveXid, TransactionId *nextXid) /* GetRunningTransactionData() acquired XidGenLock, we must release it */ LWLockRelease(XidGenLock); - *oldestActiveXid = running->oldestRunningXid; *nextXid = running->nextXid; } diff --git a/src/include/access/clog.h b/src/include/access/clog.h index 7a8918e..691430c 100644 --- a/src/include/access/clog.h +++ b/src/include/access/clog.h @@ -40,6 +40,7 @@ extern Size CLOGShmemSize(void); extern void CLOGShmemInit(void); extern void BootStrapCLOG(void); extern void StartupCLOG(void); +extern void TrimCLOG(void); extern void ShutdownCLOG(void); extern void CheckPointCLOG(void); extern void ExtendCLOG(TransactionId newestXact); diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index a11d438..7eccd5f 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -44,6 +44,7 @@ extern Snapshot GetSnapshotData(Snapshot snapshot); extern bool TransactionIdIsInProgress(TransactionId xid); extern bool TransactionIdIsActive(TransactionId xid); extern TransactionId GetOldestXmin(bool allDbs, bool ignoreVacuum); +extern TransactionId GetOldestActiveTransactionId(void); extern int GetTransactionsInCommit(TransactionId **xids_p); extern bool HaveTransactionsInCommit(TransactionId *xids, int nxids); diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 6ebac62..e587a5c 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -111,6 +111,6 @@ typedef RunningTransactionsData *RunningTransactions; extern void LogAccessExclusiveLock(Oid dbOid, Oid relOid); extern void LogAccessExclusiveLockPrepare(void); -extern void LogStandbySnapshot(TransactionId *oldestActiveXid, TransactionId *nextXid); +extern void LogStandbySnapshot(TransactionId *nextXid); #endif /* STANDBY_H */