Fetch XIDs atomically during vac_truncate_clog().
authorTom Lane
Tue, 24 May 2016 19:47:51 +0000 (15:47 -0400)
committerTom Lane
Tue, 24 May 2016 19:47:51 +0000 (15:47 -0400)
Because vac_update_datfrozenxid() updates datfrozenxid and datminmxid
in-place, it's unsafe to assume that successive reads of those values will
give consistent results.  Fetch each one just once to ensure sane behavior
in the minimum calculation.  Noted while reviewing Alexander Korotkov's
patch in the same area.

Discussion: <8564.1464116473@sss.pgh.pa.us>

src/backend/commands/vacuum.c

index 3bcc8b9dde419959f7dd0eca872990181b3f5fb7..f7a7b7625064133a67b13e96b05008b329255f8c 100644 (file)
@@ -1018,6 +1018,12 @@ vac_truncate_clog(TransactionId frozenXID,
    /*
     * Scan pg_database to compute the minimum datfrozenxid/datminmxid
     *
+    * Since vac_update_datfrozenxid updates datfrozenxid/datminmxid in-place,
+    * the values could change while we look at them.  Fetch each one just
+    * once to ensure sane behavior of the comparison logic.  (Here, as in
+    * many other places, we assume that fetching or updating an XID in shared
+    * storage is atomic.)
+    *
     * Note: we need not worry about a race condition with new entries being
     * inserted by CREATE DATABASE.  Any such entry will have a copy of some
     * existing DB's datfrozenxid, and that source DB cannot be ours because
@@ -1033,10 +1039,12 @@ vac_truncate_clog(TransactionId frozenXID,
 
    while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    {
-       Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
+       volatile FormData_pg_database *dbform = (Form_pg_database) GETSTRUCT(tuple);
+       TransactionId datfrozenxid = dbform->datfrozenxid;
+       TransactionId datminmxid = dbform->datminmxid;
 
-       Assert(TransactionIdIsNormal(dbform->datfrozenxid));
-       Assert(MultiXactIdIsValid(dbform->datminmxid));
+       Assert(TransactionIdIsNormal(datfrozenxid));
+       Assert(MultiXactIdIsValid(datminmxid));
 
        /*
         * If things are working properly, no database should have a
@@ -1047,21 +1055,21 @@ vac_truncate_clog(TransactionId frozenXID,
         * databases have been scanned and cleaned up.  (We will issue the
         * "already wrapped" warning if appropriate, though.)
         */
-       if (TransactionIdPrecedes(lastSaneFrozenXid, dbform->datfrozenxid) ||
-           MultiXactIdPrecedes(lastSaneMinMulti, dbform->datminmxid))
+       if (TransactionIdPrecedes(lastSaneFrozenXid, datfrozenxid) ||
+           MultiXactIdPrecedes(lastSaneMinMulti, datminmxid))
            bogus = true;
 
-       if (TransactionIdPrecedes(nextXID, dbform->datfrozenxid))
+       if (TransactionIdPrecedes(nextXID, datfrozenxid))
            frozenAlreadyWrapped = true;
-       else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
+       else if (TransactionIdPrecedes(datfrozenxid, frozenXID))
        {
-           frozenXID = dbform->datfrozenxid;
+           frozenXID = datfrozenxid;
            oldestxid_datoid = HeapTupleGetOid(tuple);
        }
 
-       if (MultiXactIdPrecedes(dbform->datminmxid, minMulti))
+       if (MultiXactIdPrecedes(datminmxid, minMulti))
        {
-           minMulti = dbform->datminmxid;
+           minMulti = datminmxid;
            minmulti_datoid = HeapTupleGetOid(tuple);
        }
    }