>From c6ab3d50e6597a24ad28be825951df79f8001f53 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Wed, 27 Nov 2013 14:58:16 +0100
Subject: [PATCH 1/3] Fix various problems around multixact vacuuming.

1) While autovacuum dutifully launched anti mxact wraparound vacuums,
the vacuum code did not make them be full table vacuums. As those
aren't capable of actually increasing relminmxid, autovacuum continued
to launch anti wraparound vacuums that didn't have effect until
relfrozenxid caused the vacuum to be a full table one via
vacuum_freeze_table_age. To fix the issue use logic for multixacts
similar to plain transactionids using the same GUCs.

2) Parts of the code used autovacuum_freeze_max_age to determine
whether anti mxact wraparound vacuums are necessary, others used a
hardcoded 200000000 leading to problems with a nondefault
autovacuum_freeze_max_age. Use the latter everywhere.

9.3+
---
 src/backend/access/transam/multixact.c | 26 ++++++++++++++++++++++--
 src/backend/commands/cluster.c         |  3 ++-
 src/backend/commands/vacuum.c          | 37 +++++++++++++++++++++-------------
 src/backend/commands/vacuumlazy.c      |  8 ++++++--
 src/include/access/multixact.h         |  1 +
 src/include/commands/vacuum.h          |  3 ++-
 6 files changed, 58 insertions(+), 20 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index e3f5cbc..3dff1bb 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -74,6 +74,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "pg_trace.h"
+#include "postmaster/autovacuum.h"
 #include "storage/lmgr.h"
 #include "storage/pmsignal.h"
 #include "storage/procarray.h"
@@ -1958,6 +1959,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 	/*
 	 * We'll refuse to continue assigning MultiXactIds once we get within 100
 	 * multi of data loss.
+	 *
+	 * Note: This differs from the magic number used in SetTransactionIdLimit()
+	 * since vacuum itself will never generate new multis.
 	 */
 	multiStopLimit = multiWrapLimit - 100;
 	if (multiStopLimit < FirstMultiXactId)
@@ -1979,9 +1983,12 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
 
 	/*
 	 * We'll start trying to force autovacuums when oldest_datminmxid gets to
-	 * be more than 200 million transactions old.
+	 * be more than autovacuum_freeze_max_age mxids old.
+	 *
+	 * It's a bit ugly to just reuse limits for xids that way, but it doesn't
+	 * seem worth adding separate GUCs for that purpose.
 	 */
-	multiVacLimit = oldest_datminmxid + 200000000;
+	multiVacLimit = oldest_datminmxid + autovacuum_freeze_max_age;
 	if (multiVacLimit < FirstMultiXactId)
 		multiVacLimit += FirstMultiXactId;
 
@@ -2367,6 +2374,21 @@ MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
 }
 
 /*
+ * Decide if id1 logically <= id2?
+ *
+ * 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.
  */
 static bool
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index f6a5bfe..e0a2188 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -859,7 +859,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
 	 */
 	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
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 27aea73..d55205e 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -384,11 +384,13 @@ vacuum_set_xid_limits(int freeze_min_age,
 					  TransactionId *oldestXmin,
 					  TransactionId *freezeLimit,
 					  TransactionId *freezeTableLimit,
-					  MultiXactId *multiXactCutoff)
+					  MultiXactId *multiXactCutoff,
+					  MultiXactId *multiTableLimit)
 {
 	int			freezemin;
 	TransactionId limit;
 	TransactionId safeLimit;
+	MultiXactId	mxLimit;
 
 	/*
 	 * We can always ignore processes running lazy vacuum.	This is because we
@@ -441,10 +443,22 @@ vacuum_set_xid_limits(int freeze_min_age,
 
 	*freezeLimit = limit;
 
+	/*
+	 * simplistic multixactid freezing: use the same freezing policy as
+	 * for Xids.
+	 */
+	mxLimit = GetOldestMultiXactId() - freezemin;
+	if (mxLimit < FirstMultiXactId)
+		mxLimit = FirstMultiXactId;
+
+	*multiXactCutoff = mxLimit;
+
 	if (freezeTableLimit != NULL)
 	{
 		int			freezetable;
 
+		Assert(multiTableLimit != 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 +473,24 @@ 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;
 
 		/*
-		 * simplistic multixactid freezing: use the same freezing policy as
-		 * for Xids
+		 * Compute mXID limit causing a full-table vacuum, being careful not
+		 * to generate an invalid mXID. We just copy the logic (and limits)
+		 * from plain XIDs here.
 		 */
-		mxLimit = GetOldestMultiXactId() - freezemin;
+		mxLimit = ReadNextMultiXactId() - freezetable;
 		if (mxLimit < FirstMultiXactId)
-			mxLimit = FirstMultiXactId;
-
-		*multiXactCutoff = mxLimit;
+			mxLimit = FirstNormalTransactionId;
+		*multiTableLimit = mxLimit;
 	}
 }
 
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 6688ab3..a57f444 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -181,6 +181,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 	bool		scan_all;		/* should we scan all pages? */
 	bool		scanned_all;	/* did we actually scan all pages? */
 	TransactionId freezeTableLimit;
+	MultiXactId multiTableLimit;
 	BlockNumber new_rel_pages;
 	double		new_rel_tuples;
 	BlockNumber new_rel_allvisible;
@@ -204,9 +205,12 @@ 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);
+						  &MultiXactCutoff, &multiTableLimit);
+
 	scan_all = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid,
-											 freezeTableLimit);
+											 freezeTableLimit) ||
+			MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
+										multiTableLimit);
 
 	vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
 
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index 8a9edde..aa5a78c 100644
--- a/src/include/access/multixact.h
+++ b/src/include/access/multixact.h
@@ -87,6 +87,7 @@ 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);
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 08bec25..1dde1d2 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -160,7 +160,8 @@ extern void vacuum_set_xid_limits(int freeze_min_age, int freeze_table_age,
 					  TransactionId *oldestXmin,
 					  TransactionId *freezeLimit,
 					  TransactionId *freezeTableLimit,
-					  MultiXactId *multiXactCutoff);
+					  MultiXactId *multiXactCutoff,
+					  MultiXactId *multTableLimit);
 extern void vac_update_datfrozenxid(void);
 extern void vacuum_delay_point(void);
 
-- 
1.8.5.rc2.dirty

