]> granicus.if.org Git - postgresql/commitdiff
Ensure no xid gaps during Hot Standby startup
authorSimon Riggs <simon@2ndQuadrant.com>
Sun, 23 Jun 2013 10:05:02 +0000 (11:05 +0100)
committerSimon Riggs <simon@2ndQuadrant.com>
Sun, 23 Jun 2013 13:50:17 +0000 (14:50 +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 df5e2f99413b27582b5ec191ce02a1fc7387fc4b..9ad3b3c962a4a2a316dd4cf7b1200b65e0638742 100644 (file)
@@ -6449,6 +6449,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 db917f8aa8db419f91b8b57a6871508dd48da04b..0f8238a9049f79d1c3e7a5f4a5e89a158a929f5e 100644 (file)
@@ -435,6 +435,28 @@ ProcArrayClearTransaction(PGPROC *proc)
        proc->subxids.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
  *
@@ -523,7 +545,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.
         */
 
        /*
@@ -595,20 +620,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;
@@ -667,6 +704,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 efe40c9ca81e604cd40d940899e3a073a5f0ec30..923fe4917db500c58ce421d39febf3677ce29d8a 100644 (file)
@@ -28,6 +28,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);