]> granicus.if.org Git - postgresql/commitdiff
Derive oldestActiveXid at correct time for Hot Standby.
authorSimon Riggs <simon@2ndQuadrant.com>
Wed, 2 Nov 2011 08:52:59 +0000 (08:52 +0000)
committerSimon Riggs <simon@2ndQuadrant.com>
Wed, 2 Nov 2011 08:52:59 +0000 (08:52 +0000)
There was a timing window between when oldestActiveXid was derived
and when it should have been derived that only shows itself under
heavy load. Move code around to ensure correct timing of derivation.
No change to StartupSUBTRANS() code, which is where this failed.

Bug report by Chris Redekop

src/backend/access/transam/xlog.c
src/backend/storage/ipc/procarray.c
src/backend/storage/ipc/standby.c
src/include/storage/procarray.h
src/include/storage/standby.h

index 906292690d6658d3b55cf38450cfbb8f8b2639ad..1612acd1409564dbd1ecf5ece5889e55b92ef7e7 100644 (file)
@@ -7181,6 +7181,16 @@ CreateCheckPoint(int flags)
        MemSet(&checkPoint, 0, sizeof(checkPoint));
        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.
@@ -7367,9 +7377,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();
 
index 292e330367e74328e336d41ce71227fead461e7a..d357f52793e4bba79f8275a9529abe43b3419ea7 100644 (file)
@@ -1509,6 +1509,63 @@ GetRunningTransactionData(void)
        return CurrentRunningXacts;
 }
 
+/*
+ * 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
  *
index e6b1fce61e617a36f60a95ce409f70770a213799..64030b76d617a947af501aaed1ba8d9fde8293ea 100644 (file)
@@ -818,7 +818,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;
@@ -848,7 +848,6 @@ LogStandbySnapshot(TransactionId *oldestActiveXid, TransactionId *nextXid)
        /* GetRunningTransactionData() acquired XidGenLock, we must release it */
        LWLockRelease(XidGenLock);
 
-       *oldestActiveXid = running->oldestRunningXid;
        *nextXid = running->nextXid;
 }
 
index 55ca71c1085afa4b22c14587bfa37b04ff40c31c..40734935b39fc9587d132031b4a13d0a7b9c0c68 100644 (file)
@@ -46,6 +46,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);
index 2df838f40fc5170089d909b5c841f82226b45163..7b56847a26ae5299b9597098490ecbdcb581b220 100644 (file)
@@ -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 */