Fix CLOG truncation code to not do the Wrong Thing when there are already
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 2 Apr 2002 05:12:00 +0000 (05:12 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 2 Apr 2002 05:12:00 +0000 (05:12 +0000)
wrapped-around databases.  The unvacuumed databases might be fine, or
they might not, but things will definitely not be fine if we remove the
wrong CLOG segments.  Per trouble report from Gary Wolfe, 1-Apr-2002.

src/backend/commands/vacuum.c

index 894a7e328559092b43b94eabfe35f2cc6874a260..a853344b9e418151837712522f8d41eb95b32e1a 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.213 2002/01/06 00:37:44 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.213.2.1 2002/04/02 05:12:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -616,10 +616,15 @@ vac_update_dbstats(Oid dbid,
 static void
 vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
 {
+   TransactionId myXID;
    Relation    relation;
    HeapScanDesc scan;
    HeapTuple   tuple;
    int32       age;
+   bool        vacuumAlreadyWrapped = false;
+   bool        frozenAlreadyWrapped = false;
+
+   myXID = GetCurrentTransactionId();
 
    relation = heap_openr(DatabaseRelationName, AccessShareLock);
 
@@ -634,28 +639,55 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
        if (!dbform->datallowconn)
            continue;
 
-       if (TransactionIdIsNormal(dbform->datvacuumxid) &&
-           TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
-           vacuumXID = dbform->datvacuumxid;
-       if (TransactionIdIsNormal(dbform->datfrozenxid) &&
-           TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
-           frozenXID = dbform->datfrozenxid;
+       if (TransactionIdIsNormal(dbform->datvacuumxid))
+       {
+           if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))
+               vacuumAlreadyWrapped = true;
+           else if (TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
+               vacuumXID = dbform->datvacuumxid;
+       }
+       if (TransactionIdIsNormal(dbform->datfrozenxid))
+       {
+           if (TransactionIdPrecedes(myXID, dbform->datfrozenxid))
+               frozenAlreadyWrapped = true;
+           else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
+               frozenXID = dbform->datfrozenxid;
+       }
    }
 
    heap_endscan(scan);
 
    heap_close(relation, AccessShareLock);
 
+   /*
+    * Do not truncate CLOG if we seem to have suffered wraparound already;
+    * the computed minimum XID might be bogus.
+    */
+   if (vacuumAlreadyWrapped)
+   {
+       elog(NOTICE, "Some databases have not been vacuumed in over 2 billion transactions."
+            "\n\tYou may have already suffered transaction-wraparound data loss.");
+       return;
+   }
+
    /* Truncate CLOG to the oldest vacuumxid */
    TruncateCLOG(vacuumXID);
 
    /* Give warning about impending wraparound problems */
-   age = (int32) (GetCurrentTransactionId() - frozenXID);
-   if (age > (int32) ((MaxTransactionId >> 3) * 3))
-       elog(NOTICE, "Some databases have not been vacuumed in %d transactions."
-            "\n\tBetter vacuum them within %d transactions,"
-            "\n\tor you may have a wraparound failure.",
-            age, (int32) (MaxTransactionId >> 1) - age);
+   if (frozenAlreadyWrapped)
+   {
+       elog(NOTICE, "Some databases have not been vacuumed in over 1 billion transactions."
+            "\n\tBetter vacuum them soon, or you may have a wraparound failure.");
+   }
+   else
+   {
+       age = (int32) (myXID - frozenXID);
+       if (age > (int32) ((MaxTransactionId >> 3) * 3))
+           elog(NOTICE, "Some databases have not been vacuumed in %d transactions."
+                "\n\tBetter vacuum them within %d transactions,"
+                "\n\tor you may have a wraparound failure.",
+                age, (int32) (MaxTransactionId >> 1) - age);
+   }
 }