]> granicus.if.org Git - postgresql/commitdiff
Ensure no xid gaps during Hot Standby startup
authorSimon Riggs <simon@2ndQuadrant.com>
Sun, 23 Jun 2013 10:09:24 +0000 (11:09 +0100)
committerSimon Riggs <simon@2ndQuadrant.com>
Sun, 23 Jun 2013 10:09:24 +0000 (11:09 +0100)
In some cases with higher numbers of subtransactions
it was possible for us to incorrectly initialize
subtrans leading to complaints of missing pages.

Bug report by Sergey Konoplev
Analysis and fix by Andres Freund

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

index 9f858995d1218137465eba78689a3daef963dfb8..664470336958796c04000e540085342c2af0ef9d 100644 (file)
@@ -5391,6 +5391,9 @@ StartupXLOG(void)
                                oldestActiveXID = checkPoint.oldestActiveXid;
                        Assert(TransactionIdIsValid(oldestActiveXID));
 
+                       /* Tell procarray about the range of xids it has to deal with */
+                       ProcArrayInitRecovery(ShmemVariableCache->nextXid);
+
                        /*
                         * Startup commit log and subtrans only. Other SLRUs are not
                         * maintained during recovery and need not be started yet.
index b5f66fbfb0415dcd4acaabb8c2671a6d883bf233..c2f86ff2c4a7cc1bbb3dabbd5a39fe053ef6e223 100644 (file)
@@ -469,6 +469,28 @@ ProcArrayClearTransaction(PGPROC *proc)
        pgxact->overflowed = false;
 }
 
+/*
+ * ProcArrayInitRecovery -- initialize recovery xid mgmt environment
+ *
+ * Remember up to where the startup process initialized the CLOG and subtrans
+ * so we can ensure its initialized gaplessly up to the point where necessary
+ * while in recovery.
+ */
+void
+ProcArrayInitRecovery(TransactionId initializedUptoXID)
+{
+       Assert(standbyState == STANDBY_INITIALIZED);
+       Assert(TransactionIdIsNormal(initializedUptoXID));
+
+       /*
+        * we set latestObservedXid to the xid SUBTRANS has been initialized upto
+        * so we can extend it from that point onwards when we reach a consistent
+        * state in ProcArrayApplyRecoveryInfo().
+        */
+       latestObservedXid = initializedUptoXID;
+       TransactionIdRetreat(latestObservedXid);
+}
+
 /*
  * ProcArrayApplyRecoveryInfo -- apply recovery info about xids
  *
@@ -564,7 +586,10 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
        Assert(standbyState == STANDBY_INITIALIZED);
 
        /*
-        * OK, we need to initialise from the RunningTransactionsData record
+        * OK, we need to initialise from the RunningTransactionsData record.
+        *
+        * NB: this can be reached at least twice, so make sure new code can deal
+        * with that.
         */
 
        /*
@@ -636,20 +661,32 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
        pfree(xids);
 
        /*
+        * latestObservedXid is set to the the point where SUBTRANS was started up
+        * to, initialize subtrans from thereon, up to nextXid - 1.
+        */
+       Assert(TransactionIdIsNormal(latestObservedXid));
+       while (TransactionIdPrecedes(latestObservedXid, running->nextXid))
+       {
+               ExtendCLOG(latestObservedXid);
+               ExtendSUBTRANS(latestObservedXid);
+
+               TransactionIdAdvance(latestObservedXid);
+       }
+
+       /* ----------
         * Now we've got the running xids we need to set the global values that
         * are used to track snapshots as they evolve further.
         *
-        * - latestCompletedXid which will be the xmax for snapshots -
-        * lastOverflowedXid which shows whether snapshots overflow - nextXid
+        * - latestCompletedXid which will be the xmax for snapshots
+        * - lastOverflowedXid which shows whether snapshots overflow
+        * - nextXid
         *
         * If the snapshot overflowed, then we still initialise with what we know,
         * but the recovery snapshot isn't fully valid yet because we know there
         * are some subxids missing. We don't know the specific subxids that are
         * missing, so conservatively assume the last one is latestObservedXid.
+        * ----------
         */
-       latestObservedXid = running->nextXid;
-       TransactionIdRetreat(latestObservedXid);
-
        if (running->subxid_overflow)
        {
                standbyState = STANDBY_SNAPSHOT_PENDING;
@@ -719,6 +756,10 @@ ProcArrayApplyXidAssignment(TransactionId topxid,
 
        Assert(standbyState >= STANDBY_INITIALIZED);
 
+       /* can't do anything useful unless we have more state setup */
+       if (standbyState == STANDBY_INITIALIZED)
+               return;
+
        max_xid = TransactionIdLatest(topxid, nsubxids, subxids);
 
        /*
index d5fdfea6f20b2c3ef9c2d27664499a47a5615ac0..c5f58b413db73ed4b74534f5289058a612126780 100644 (file)
@@ -26,6 +26,7 @@ extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid);
 extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid);
 extern void ProcArrayClearTransaction(PGPROC *proc);
 
+extern void ProcArrayInitRecovery(TransactionId initializedUptoXID);
 extern void ProcArrayApplyRecoveryInfo(RunningTransactions running);
 extern void ProcArrayApplyXidAssignment(TransactionId topxid,
                                                        int nsubxids, TransactionId *subxids);