@@ -204,6 +204,7 @@ typedef struct MultiXactStateData
204204 */
205205 MultiXactId oldestMultiXactId ;
206206 Oid oldestMultiXactDB ;
207+ MultiXactOffset oldestOffset ;
207208
208209 /*
209210 * This is what the previous checkpoint stored as the truncate position.
@@ -954,14 +955,17 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
954955 * against catastrophic data loss due to multixact wraparound. The basic
955956 * rules are:
956957 *
957- * If we're past multiVacLimit, start trying to force autovacuum cycles.
958+ * If we're past multiVacLimit or the safe threshold for member storage space,
959+ * start trying to force autovacuum cycles.
958960 * If we're past multiWarnLimit, start issuing warnings.
959961 * If we're past multiStopLimit, refuse to create new MultiXactIds.
960962 *
961963 * Note these are pretty much the same protections in GetNewTransactionId.
962964 *----------
963965 */
964- if (!MultiXactIdPrecedes (result , MultiXactState -> multiVacLimit ))
966+ if (!MultiXactIdPrecedes (result , MultiXactState -> multiVacLimit ) ||
967+ (MultiXactState -> nextOffset - MultiXactState -> oldestOffset
968+ > MULTIXACT_MEMBER_SAFE_THRESHOLD ))
965969 {
966970 /*
967971 * For safety's sake, we release MultiXactGenLock while sending
@@ -2142,6 +2146,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
21422146 MultiXactId multiStopLimit ;
21432147 MultiXactId multiWrapLimit ;
21442148 MultiXactId curMulti ;
2149+ MultiXactOffset oldestOffset ;
2150+ MultiXactOffset nextOffset ;
21452151
21462152 Assert (MultiXactIdIsValid (oldest_datminmxid ));
21472153
@@ -2194,6 +2200,35 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
21942200 if (multiVacLimit < FirstMultiXactId )
21952201 multiVacLimit += FirstMultiXactId ;
21962202
2203+ /*
2204+ * Determine the offset of the oldest multixact that might still be
2205+ * referenced. Normally, we can read the offset from the multixact itself,
2206+ * but there's an important special case: if there are no multixacts in
2207+ * existence at all, oldest_datminmxid obviously can't point to one. It
2208+ * will instead point to the multixact ID that will be assigned the next
2209+ * time one is needed.
2210+ *
2211+ * NB: oldest_dataminmxid is the oldest multixact that might still be
2212+ * referenced from a table, unlike in DetermineSafeOldestOffset, where we
2213+ * do this same computation based on the oldest value that might still
2214+ * exist in the SLRU. This is because here we're trying to compute a
2215+ * threshold for activating autovacuum, which can only remove references
2216+ * to multixacts, whereas there we are computing a threshold for creating
2217+ * new multixacts, which requires the old ones to have first been
2218+ * truncated away by a checkpoint.
2219+ */
2220+ LWLockAcquire (MultiXactGenLock , LW_SHARED );
2221+ if (MultiXactState -> nextMXact == oldest_datminmxid )
2222+ {
2223+ oldestOffset = MultiXactState -> nextOffset ;
2224+ LWLockRelease (MultiXactGenLock );
2225+ }
2226+ else
2227+ {
2228+ LWLockRelease (MultiXactGenLock );
2229+ oldestOffset = find_multixact_start (oldest_datminmxid );
2230+ }
2231+
21972232 /* Grab lock for just long enough to set the new limit values */
21982233 LWLockAcquire (MultiXactGenLock , LW_EXCLUSIVE );
21992234 MultiXactState -> oldestMultiXactId = oldest_datminmxid ;
@@ -2202,7 +2237,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
22022237 MultiXactState -> multiWarnLimit = multiWarnLimit ;
22032238 MultiXactState -> multiStopLimit = multiStopLimit ;
22042239 MultiXactState -> multiWrapLimit = multiWrapLimit ;
2240+ MultiXactState -> oldestOffset = oldestOffset ;
22052241 curMulti = MultiXactState -> nextMXact ;
2242+ nextOffset = MultiXactState -> nextOffset ;
22062243 LWLockRelease (MultiXactGenLock );
22072244
22082245 /* Log the info */
@@ -2217,7 +2254,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
22172254 * database, it'll call here, and we'll signal the postmaster to start
22182255 * another iteration immediately if there are still any old databases.
22192256 */
2220- if (MultiXactIdPrecedes (multiVacLimit , curMulti ) &&
2257+ if ((MultiXactIdPrecedes (multiVacLimit , curMulti ) ||
2258+ (nextOffset - oldestOffset > MULTIXACT_MEMBER_SAFE_THRESHOLD )) &&
22212259 IsUnderPostmaster && !InRecovery )
22222260 SendPostmasterSignal (PMSIGNAL_START_AUTOVAC_LAUNCHER );
22232261
@@ -2476,11 +2514,16 @@ DetermineSafeOldestOffset(MultiXactId oldestMXact)
24762514 MultiXactOffset oldestOffset ;
24772515
24782516 /*
2479- * We determine the safe upper bound for offsets of new xacts by reading
2480- * the offset of the oldest multixact, and going back one segment. This
2481- * way, the sequence of multixact member segments will always have a
2482- * one-segment hole at a minimum. We start spewing warnings a few
2483- * complete segments before that.
2517+ * Determine the offset of the oldest multixact. Normally, we can read
2518+ * the offset from the multixact itself, but there's an important special
2519+ * case: if there are no multixacts in existence at all, oldestMXact
2520+ * obviously can't point to one. It will instead point to the multixact
2521+ * ID that will be assigned the next time one is needed.
2522+ *
2523+ * NB: oldestMXact should be the oldest multixact that still exists in
2524+ * the SLRU, unlike in SetMultiXactIdLimit, where we do this same
2525+ * computation based on the oldest value that might be referenced in a
2526+ * table.
24842527 */
24852528 LWLockAcquire (MultiXactGenLock , LW_SHARED );
24862529 if (MultiXactState -> nextMXact == oldestMXact )
@@ -2594,9 +2637,9 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
25942637 nextOffset = MultiXactState -> nextOffset ;
25952638 oldestMultiXactId = MultiXactState -> oldestMultiXactId ;
25962639 nextMultiXactId = MultiXactState -> nextMXact ;
2640+ oldestOffset = MultiXactState -> oldestOffset ;
25972641 LWLockRelease (MultiXactGenLock );
25982642
2599- oldestOffset = find_multixact_start (oldestMultiXactId );
26002643 * members = nextOffset - oldestOffset ;
26012644 * multixacts = nextMultiXactId - oldestMultiXactId ;
26022645}
0 commit comments