@@ -202,6 +202,7 @@ typedef struct MultiXactStateData
202
202
*/
203
203
MultiXactId oldestMultiXactId ;
204
204
Oid oldestMultiXactDB ;
205
+ MultiXactOffset oldestOffset ;
205
206
206
207
/*
207
208
* This is what the previous checkpoint stored as the truncate position.
@@ -959,14 +960,17 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
959
960
* against catastrophic data loss due to multixact wraparound. The basic
960
961
* rules are:
961
962
*
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.
963
965
* If we're past multiWarnLimit, start issuing warnings.
964
966
* If we're past multiStopLimit, refuse to create new MultiXactIds.
965
967
*
966
968
* Note these are pretty much the same protections in GetNewTransactionId.
967
969
*----------
968
970
*/
969
- if (!MultiXactIdPrecedes (result , MultiXactState -> multiVacLimit ))
971
+ if (!MultiXactIdPrecedes (result , MultiXactState -> multiVacLimit ) ||
972
+ (MultiXactState -> nextOffset - MultiXactState -> oldestOffset
973
+ > MULTIXACT_MEMBER_SAFE_THRESHOLD ))
970
974
{
971
975
/*
972
976
* For safety's sake, we release MultiXactGenLock while sending
@@ -2161,6 +2165,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
2161
2165
MultiXactId multiStopLimit ;
2162
2166
MultiXactId multiWrapLimit ;
2163
2167
MultiXactId curMulti ;
2168
+ MultiXactOffset oldestOffset ;
2169
+ MultiXactOffset nextOffset ;
2164
2170
2165
2171
Assert (MultiXactIdIsValid (oldest_datminmxid ));
2166
2172
@@ -2213,6 +2219,35 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
2213
2219
if (multiVacLimit < FirstMultiXactId )
2214
2220
multiVacLimit += FirstMultiXactId ;
2215
2221
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
+
2216
2251
/* Grab lock for just long enough to set the new limit values */
2217
2252
LWLockAcquire (MultiXactGenLock , LW_EXCLUSIVE );
2218
2253
MultiXactState -> oldestMultiXactId = oldest_datminmxid ;
@@ -2221,7 +2256,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
2221
2256
MultiXactState -> multiWarnLimit = multiWarnLimit ;
2222
2257
MultiXactState -> multiStopLimit = multiStopLimit ;
2223
2258
MultiXactState -> multiWrapLimit = multiWrapLimit ;
2259
+ MultiXactState -> oldestOffset = oldestOffset ;
2224
2260
curMulti = MultiXactState -> nextMXact ;
2261
+ nextOffset = MultiXactState -> nextOffset ;
2225
2262
LWLockRelease (MultiXactGenLock );
2226
2263
2227
2264
/* Log the info */
@@ -2236,7 +2273,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
2236
2273
* database, it'll call here, and we'll signal the postmaster to start
2237
2274
* another iteration immediately if there are still any old databases.
2238
2275
*/
2239
- if (MultiXactIdPrecedes (multiVacLimit , curMulti ) &&
2276
+ if ((MultiXactIdPrecedes (multiVacLimit , curMulti ) ||
2277
+ (nextOffset - oldestOffset > MULTIXACT_MEMBER_SAFE_THRESHOLD )) &&
2240
2278
IsUnderPostmaster && !InRecovery )
2241
2279
SendPostmasterSignal (PMSIGNAL_START_AUTOVAC_LAUNCHER );
2242
2280
@@ -2495,11 +2533,16 @@ DetermineSafeOldestOffset(MultiXactId oldestMXact)
2495
2533
MultiXactOffset oldestOffset ;
2496
2534
2497
2535
/*
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.
2503
2546
*/
2504
2547
LWLockAcquire (MultiXactGenLock , LW_SHARED );
2505
2548
if (MultiXactState -> nextMXact == oldestMXact )
@@ -2613,9 +2656,9 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
2613
2656
nextOffset = MultiXactState -> nextOffset ;
2614
2657
oldestMultiXactId = MultiXactState -> oldestMultiXactId ;
2615
2658
nextMultiXactId = MultiXactState -> nextMXact ;
2659
+ oldestOffset = MultiXactState -> oldestOffset ;
2616
2660
LWLockRelease (MultiXactGenLock );
2617
2661
2618
- oldestOffset = find_multixact_start (oldestMultiXactId );
2619
2662
* members = nextOffset - oldestOffset ;
2620
2663
* multixacts = nextMultiXactId - oldestMultiXactId ;
2621
2664
}
0 commit comments