]> granicus.if.org Git - postgresql/commitdiff
Fix full-table-vacuum request mechanism for MultiXactIds
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 28 Nov 2013 19:52:54 +0000 (16:52 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Sat, 30 Nov 2013 00:48:11 +0000 (21:48 -0300)
While autovacuum dutifully launched anti-multixact-wraparound vacuums
when the multixact "age" was reached, the vacuum code was not aware that
it needed to make them be full table vacuums.  As the resulting
partial-table vacuums aren't capable of actually increasing relminmxid,
autovacuum continued to launch anti-wraparound vacuums that didn't have
the intended effect, until age of relfrozenxid caused the vacuum to
finally be a full table one via vacuum_freeze_table_age.

To fix, introduce logic for multixacts similar to that for plain
TransactionIds, using the same GUCs.

Backpatch to 9.3, where permanent MultiXactIds were introduced.

Andres Freund, some cleanup by Álvaro

src/backend/access/transam/multixact.c
src/backend/commands/cluster.c
src/backend/commands/vacuum.c
src/backend/commands/vacuumlazy.c
src/include/access/multixact.h
src/include/commands/vacuum.h

index de0193aaf6453d2b74e275c6cc207f262389e29d..90fa030caf2c10c2490024d0b4a2ae35bf7485c9 100644 (file)
@@ -2374,6 +2374,21 @@ MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
        return (diff < 0);
 }
 
+/*
+ * MultiXactIdPrecedesOrEquals -- is multi1 logically <= multi2?
+ *
+ * XXX do we need to do something special for InvalidMultiXactId?
+ * (Doesn't look like it.)
+ */
+bool
+MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
+{
+       int32           diff = (int32) (multi1 - multi2);
+
+       return (diff <= 0);
+}
+
+
 /*
  * Decide which of two offsets is earlier.
  */
index 08615e38c62f78b7bc90ba1da8291255bbe1a7d2..1cdb22048a43956c5cccd2b4047f138ee394ed68 100644 (file)
@@ -175,7 +175,10 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
                /* close relation, keep lock till commit */
                heap_close(rel, NoLock);
 
-               /* Do the job */
+               /*
+                * Do the job.  We use a -1 freeze_min_age to avoid having CLUSTER
+                * freeze tuples earlier than a plain VACUUM would.
+                */
                cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1);
        }
        else
@@ -225,6 +228,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
                        StartTransactionCommand();
                        /* functions in indexes may want a snapshot set */
                        PushActiveSnapshot(GetTransactionSnapshot());
+                       /* Do the job.  As above, use a -1 freeze_min_age. */
                        cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose,
                                                -1, -1);
                        PopActiveSnapshot();
@@ -842,13 +846,12 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
                *pSwapToastByContent = false;
 
        /*
-        * compute xids used to freeze and weed out dead tuples.  We use -1
-        * freeze_min_age to avoid having CLUSTER freeze tuples earlier than a
-        * plain VACUUM would.
+        * compute xids used to freeze and weed out dead tuples.
         */
        vacuum_set_xid_limits(freeze_min_age, freeze_table_age,
                                                  OldHeap->rd_rel->relisshared,
-                                                 &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff);
+                                                 &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
+                                                 NULL);
 
        /*
         * FreezeXid will become the table's new relfrozenxid, and that mustn't go
index 56c35c86a195e22138337e9467f1fbc4c01f1f23..d50333f24c830b68e535722e12e0473fc44b4fa7 100644 (file)
@@ -376,6 +376,24 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
 
 /*
  * vacuum_set_xid_limits() -- compute oldest-Xmin and freeze cutoff points
+ *
+ * The output parameters are:
+ * - oldestXmin is the cutoff value used to distinguish whether tuples are
+ *   DEAD or RECENTLY_DEAD (see HeapTupleSatisfiesVacuum).
+ * - freezeLimit is the Xid below which all Xids are replaced by
+ *   FrozenTransactionId during vacuum.
+ * - xidFullScanLimit (computed from the the table_freeze_age parameter)
+ *   represents a minimum Xid value; a table whose relfrozenxid is older than
+ *   this will have a full-table vacuum applied to it, to freeze tuples across
+ *   the whole table.  Vacuuming a table younger than this value can use a
+ *   partial scan.
+ * - multiXactCutoff is the value below which all MultiXactIds are removed from
+ *   Xmax.
+ * - mxactFullScanLimit is a value against which a table's relminmxid value is
+ *   compared to produce a full-table vacuum, as with xidFullScanLimit.
+ *
+ * xidFullScanLimit and mxactFullScanLimit can be passed as NULL if caller is
+ * not interested.
  */
 void
 vacuum_set_xid_limits(int freeze_min_age,
@@ -383,12 +401,14 @@ vacuum_set_xid_limits(int freeze_min_age,
                                          bool sharedRel,
                                          TransactionId *oldestXmin,
                                          TransactionId *freezeLimit,
-                                         TransactionId *freezeTableLimit,
-                                         MultiXactId *multiXactCutoff)
+                                         TransactionId *xidFullScanLimit,
+                                         MultiXactId *multiXactCutoff,
+                                         MultiXactId *mxactFullScanLimit)
 {
        int                     freezemin;
        TransactionId limit;
        TransactionId safeLimit;
+       MultiXactId     mxactLimit;
 
        /*
         * We can always ignore processes running lazy vacuum.  This is because we
@@ -441,10 +461,22 @@ vacuum_set_xid_limits(int freeze_min_age,
 
        *freezeLimit = limit;
 
-       if (freezeTableLimit != NULL)
+       /*
+        * simplistic MultiXactId removal limit: use the same policy as for
+        * freezing Xids (except we use the oldest known mxact instead of the
+        * current next value).
+        */
+       mxactLimit = GetOldestMultiXactId() - freezemin;
+       if (mxactLimit < FirstMultiXactId)
+               mxactLimit = FirstMultiXactId;
+       *multiXactCutoff = mxactLimit;
+
+       if (xidFullScanLimit != NULL)
        {
                int                     freezetable;
 
+               Assert(mxactFullScanLimit != NULL);
+
                /*
                 * Determine the table freeze age to use: as specified by the caller,
                 * or vacuum_freeze_table_age, but in any case not more than
@@ -459,29 +491,25 @@ vacuum_set_xid_limits(int freeze_min_age,
                Assert(freezetable >= 0);
 
                /*
-                * Compute the cutoff XID, being careful not to generate a "permanent"
-                * XID.
+                * Compute XID limit causing a full-table vacuum, being careful not to
+                * generate a "permanent" XID.
                 */
                limit = ReadNewTransactionId() - freezetable;
                if (!TransactionIdIsNormal(limit))
                        limit = FirstNormalTransactionId;
 
-               *freezeTableLimit = limit;
-       }
-
-       if (multiXactCutoff != NULL)
-       {
-               MultiXactId mxLimit;
+               *xidFullScanLimit = limit;
 
                /*
-                * simplistic multixactid freezing: use the same freezing policy as
-                * for Xids
+                * Compute MultiXactId limit to cause a full-table vacuum, being
+                * careful not to generate an invalid multi. We just copy the logic
+                * (and limits) from plain XIDs here.
                 */
-               mxLimit = GetOldestMultiXactId() - freezemin;
-               if (mxLimit < FirstMultiXactId)
-                       mxLimit = FirstMultiXactId;
+               mxactLimit = ReadNextMultiXactId() - freezetable;
+               if (mxactLimit < FirstMultiXactId)
+                       mxactLimit = FirstMultiXactId;
 
-               *multiXactCutoff = mxLimit;
+               *mxactFullScanLimit = mxactLimit;
        }
 }
 
index 93ea08512428485e654823a9305b95257b50bf35..ff6bd8e5b06e1c2de31a6bfbdb4389b81395af5c 100644 (file)
@@ -180,7 +180,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
                                write_rate;
        bool            scan_all;               /* should we scan all pages? */
        bool            scanned_all;    /* did we actually scan all pages? */
-       TransactionId freezeTableLimit;
+       TransactionId xidFullScanLimit;
+       MultiXactId mxactFullScanLimit;
        BlockNumber new_rel_pages;
        double          new_rel_tuples;
        BlockNumber new_rel_allvisible;
@@ -203,10 +204,19 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 
        vacuum_set_xid_limits(vacstmt->freeze_min_age, vacstmt->freeze_table_age,
                                                  onerel->rd_rel->relisshared,
-                                                 &OldestXmin, &FreezeLimit, &freezeTableLimit,
-                                                 &MultiXactCutoff);
+                                                 &OldestXmin, &FreezeLimit, &xidFullScanLimit,
+                                                 &MultiXactCutoff, &mxactFullScanLimit);
+
+       /*
+        * We request a full scan if either the table's frozen Xid is now older
+        * than or equal to the requested Xid full-table scan limit; or if the
+        * table's minimum MultiXactId is older than or equal to the requested mxid
+        * full-table scan limit.
+        */
        scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
-                                                                                        freezeTableLimit);
+                                                                                        xidFullScanLimit);
+       scan_all |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
+                                                                                       mxactFullScanLimit);
 
        vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
 
index 8a9eddee387c7261d22b1f8eb0f4da7fc5dbb56e..e6db81a8270b3b815e97003014f76481bfca088b 100644 (file)
@@ -87,6 +87,8 @@ extern void MultiXactIdSetOldestMember(void);
 extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids,
                                          bool allow_old);
 extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
+extern bool MultiXactIdPrecedesOrEquals(MultiXactId multi1,
+                                                       MultiXactId multi2);
 
 extern void AtEOXact_MultiXact(void);
 extern void AtPrepare_MultiXact(void);
index 08bec256ba855d7c105672ce93da0d3b6efb19fb..44a3c3bd52e3fec48b15b8b5dc580b1f347eb004 100644 (file)
@@ -159,8 +159,9 @@ extern void vacuum_set_xid_limits(int freeze_min_age, int freeze_table_age,
                                          bool sharedRel,
                                          TransactionId *oldestXmin,
                                          TransactionId *freezeLimit,
-                                         TransactionId *freezeTableLimit,
-                                         MultiXactId *multiXactCutoff);
+                                         TransactionId *xidFullScanLimit,
+                                         MultiXactId *multiXactCutoff,
+                                         MultiXactId *mxactFullScanLimit);
 extern void vac_update_datfrozenxid(void);
 extern void vacuum_delay_point(void);