@@ -202,6 +202,7 @@ typedef struct MultiXactStateData
202202 */
203203MultiXactId oldestMultiXactId ;
204204Oid oldestMultiXactDB ;
205+ MultiXactOffset oldestOffset ;
205206
206207/*
207208 * This is what the previous checkpoint stored as the truncate position.
@@ -959,14 +960,17 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
959960 * against catastrophic data loss due to multixact wraparound. The basic
960961 * rules are:
961962 *
962- * If we're past multiVacLimit, start trying to force autovacuum cycles.
963+ * If we're past multiVacLimit or the safe threshold for member storage space,
964+ * start trying to force autovacuum cycles.
963965 * If we're past multiWarnLimit, start issuing warnings.
964966 * If we're past multiStopLimit, refuse to create new MultiXactIds.
965967 *
966968 * Note these are pretty much the same protections in GetNewTransactionId.
967969 *----------
968970 */
969- if (!MultiXactIdPrecedes (result ,MultiXactState -> multiVacLimit ))
971+ if (!MultiXactIdPrecedes (result ,MultiXactState -> multiVacLimit )||
972+ (MultiXactState -> nextOffset - MultiXactState -> oldestOffset
973+ > MULTIXACT_MEMBER_SAFE_THRESHOLD ))
970974{
971975/*
972976 * For safety's sake, we release MultiXactGenLock while sending
@@ -2161,6 +2165,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
21612165MultiXactId multiStopLimit ;
21622166MultiXactId multiWrapLimit ;
21632167MultiXactId curMulti ;
2168+ MultiXactOffset oldestOffset ;
2169+ MultiXactOffset nextOffset ;
21642170
21652171Assert (MultiXactIdIsValid (oldest_datminmxid ));
21662172
@@ -2213,6 +2219,35 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
22132219if (multiVacLimit < FirstMultiXactId )
22142220multiVacLimit += FirstMultiXactId ;
22152221
2222+ /*
2223+ * Determine the offset of the oldest multixact that might still be
2224+ * referenced. Normally, we can read the offset from the multixact itself,
2225+ * but there's an important special case: if there are no multixacts in
2226+ * existence at all, oldest_datminmxid obviously can't point to one. It
2227+ * will instead point to the multixact ID that will be assigned the next
2228+ * time one is needed.
2229+ *
2230+ * NB: oldest_dataminmxid is the oldest multixact that might still be
2231+ * referenced from a table, unlike in DetermineSafeOldestOffset, where we
2232+ * do this same computation based on the oldest value that might still
2233+ * exist in the SLRU. This is because here we're trying to compute a
2234+ * threshold for activating autovacuum, which can only remove references
2235+ * to multixacts, whereas there we are computing a threshold for creating
2236+ * new multixacts, which requires the old ones to have first been
2237+ * truncated away by a checkpoint.
2238+ */
2239+ LWLockAcquire (MultiXactGenLock ,LW_SHARED );
2240+ if (MultiXactState -> nextMXact == oldest_datminmxid )
2241+ {
2242+ oldestOffset = MultiXactState -> nextOffset ;
2243+ LWLockRelease (MultiXactGenLock );
2244+ }
2245+ else
2246+ {
2247+ LWLockRelease (MultiXactGenLock );
2248+ oldestOffset = find_multixact_start (oldest_datminmxid );
2249+ }
2250+
22162251/* Grab lock for just long enough to set the new limit values */
22172252LWLockAcquire (MultiXactGenLock ,LW_EXCLUSIVE );
22182253MultiXactState -> oldestMultiXactId = oldest_datminmxid ;
@@ -2221,7 +2256,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
22212256MultiXactState -> multiWarnLimit = multiWarnLimit ;
22222257MultiXactState -> multiStopLimit = multiStopLimit ;
22232258MultiXactState -> multiWrapLimit = multiWrapLimit ;
2259+ MultiXactState -> oldestOffset = oldestOffset ;
22242260curMulti = MultiXactState -> nextMXact ;
2261+ nextOffset = MultiXactState -> nextOffset ;
22252262LWLockRelease (MultiXactGenLock );
22262263
22272264/* Log the info */
@@ -2236,7 +2273,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
22362273 * database, it'll call here, and we'll signal the postmaster to start
22372274 * another iteration immediately if there are still any old databases.
22382275 */
2239- if (MultiXactIdPrecedes (multiVacLimit ,curMulti )&&
2276+ if ((MultiXactIdPrecedes (multiVacLimit ,curMulti )||
2277+ (nextOffset - oldestOffset > MULTIXACT_MEMBER_SAFE_THRESHOLD ))&&
22402278IsUnderPostmaster && !InRecovery )
22412279SendPostmasterSignal (PMSIGNAL_START_AUTOVAC_LAUNCHER );
22422280
@@ -2495,11 +2533,16 @@ DetermineSafeOldestOffset(MultiXactId oldestMXact)
24952533MultiXactOffset oldestOffset ;
24962534
24972535/*
2498- * We determine the safe upper bound for offsets of new xacts by reading
2499- * the offset of the oldest multixact, and going back one segment. This
2500- * way, the sequence of multixact member segments will always have a
2501- * one-segment hole at a minimum. We start spewing warnings a few
2502- * complete segments before that.
2536+ * Determine the offset of the oldest multixact. Normally, we can read
2537+ * the offset from the multixact itself, but there's an important special
2538+ * case: if there are no multixacts in existence at all, oldestMXact
2539+ * obviously can't point to one. It will instead point to the multixact
2540+ * ID that will be assigned the next time one is needed.
2541+ *
2542+ * NB: oldestMXact should be the oldest multixact that still exists in
2543+ * the SLRU, unlike in SetMultiXactIdLimit, where we do this same
2544+ * computation based on the oldest value that might be referenced in a
2545+ * table.
25032546 */
25042547LWLockAcquire (MultiXactGenLock ,LW_SHARED );
25052548if (MultiXactState -> nextMXact == oldestMXact )
@@ -2613,9 +2656,9 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
26132656nextOffset = MultiXactState -> nextOffset ;
26142657oldestMultiXactId = MultiXactState -> oldestMultiXactId ;
26152658nextMultiXactId = MultiXactState -> nextMXact ;
2659+ oldestOffset = MultiXactState -> oldestOffset ;
26162660LWLockRelease (MultiXactGenLock );
26172661
2618- oldestOffset = find_multixact_start (oldestMultiXactId );
26192662* members = nextOffset - oldestOffset ;
26202663* multixacts = nextMultiXactId - oldestMultiXactId ;
26212664}