]> 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:41:07 +0000 (14:41 +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 42fa6aa7e9c3c0b7da34f42da27ba5fd1b297cf5..8243ea62d7834d79d07406ae3904eaf724dd6436 100644 (file)
@@ -6692,6 +6692,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 371b1045a13d20c0efcc69a18fc16f6e6d505af7..6cc115090b74375b21dbea703003b4ba1325f6fe 100644 (file)
@@ -468,6 +468,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
  *
@@ -556,7 +578,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.
         */
 
        /*
@@ -628,20 +653,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;
@@ -711,6 +748,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 5b4cab926f58dda816b6b9dfa9b93fedb6e33fce..1819be74f8ce95aed81087dce4317fe6a770c76f 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);