]> granicus.if.org Git - postgresql/commitdiff
Derive oldestActiveXid at correct time for Hot Standby.
authorSimon Riggs <simon@2ndQuadrant.com>
Wed, 2 Nov 2011 08:53:40 +0000 (08:53 +0000)
committerSimon Riggs <simon@2ndQuadrant.com>
Wed, 2 Nov 2011 08:53:40 +0000 (08:53 +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 60ea3f7f1ed651450c2e2fd252ed897112157a00..f3a8dae63122c16d1264424eb9d033b03eca1631 100644 (file)
@@ -7598,6 +7598,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.
@@ -7784,9 +7794,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 878b64715c7e3fd149d6ea77f02bf59aa275ca0e..00dc9c853c93d6443c5c73af6f6361a14acebf28 100644 (file)
@@ -1528,6 +1528,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 bf92d259950c38868d3cd4898c0943865e6fadb6..f39062204c014c09d97bad03ed70d4e78d9c29e2 100644 (file)
@@ -814,7 +814,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;
@@ -844,7 +844,6 @@ LogStandbySnapshot(TransactionId *oldestActiveXid, TransactionId *nextXid)
        /* GetRunningTransactionData() acquired XidGenLock, we must release it */
        LWLockRelease(XidGenLock);
 
-       *oldestActiveXid = running->oldestRunningXid;
        *nextXid = running->nextXid;
 }
 
index 3c20fc48f67670d1df026277c7bccf95f1225107..efe40c9ca81e604cd40d940899e3a073a5f0ec30 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 6ebac62db5413d2fcaf26d658808656c90451b6d..e587a5c4a4dcb48d7c2d15c031590788ce2912b0 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 */