diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index be94a87..dd056cd 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -204,6 +204,7 @@ typedef struct MultiXactStateData */ MultiXactId oldestMultiXactId; Oid oldestMultiXactDB; + MultiXactOffset oldestOffset; /* * This is what the previous checkpoint stored as the truncate position. @@ -954,14 +955,17 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset) * against catastrophic data loss due to multixact wraparound. The basic * rules are: * - * If we're past multiVacLimit, start trying to force autovacuum cycles. + * If we're past multiVacLimit or the safe threshold for member storage space, + * start trying to force autovacuum cycles. * If we're past multiWarnLimit, start issuing warnings. * If we're past multiStopLimit, refuse to create new MultiXactIds. * * Note these are pretty much the same protections in GetNewTransactionId. *---------- */ - if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit)) + if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit) || + (MultiXactState->nextOffset - MultiXactState->oldestOffset + > MULTIXACT_MEMBER_SAFE_THRESHOLD)) { /* * For safety's sake, we release MultiXactGenLock while sending @@ -2142,6 +2146,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) MultiXactId multiStopLimit; MultiXactId multiWrapLimit; MultiXactId curMulti; + MultiXactOffset oldestOffset; + MultiXactOffset nextOffset; Assert(MultiXactIdIsValid(oldest_datminmxid)); @@ -2194,6 +2200,30 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) if (multiVacLimit < FirstMultiXactId) multiVacLimit += FirstMultiXactId; + /* + * Find the member offset of the oldest multixact, if there is one, which + * future vacuums and autovacuums will use to judge the amount of member + * space that is used. + */ + LWLockAcquire(MultiXactGenLock, LW_SHARED); + curMulti = MultiXactState->nextMXact; + if (oldest_datminmxid == curMulti) + { + /* + * There are currently no multixacts, so we use the next offset as the + * oldest offset. While oldestOffset remains equal to nextOffset, + * GetNewMultiXactId and autovacuum will know that member space is + * entirely empty. After we release the lock, if a new multixact is + * created, its member offset will be nextOffset and nextOffset will + * be advanced, so even then this will be the correct value. + */ + oldestOffset = MultiXactState->nextOffset; + } + LWLockRelease(MultiXactGenLock); + + if (oldest_datminmxid != curMulti) + oldestOffset = find_multixact_start(oldest_datminmxid); + /* Grab lock for just long enough to set the new limit values */ LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); MultiXactState->oldestMultiXactId = oldest_datminmxid; @@ -2202,7 +2232,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) MultiXactState->multiWarnLimit = multiWarnLimit; MultiXactState->multiStopLimit = multiStopLimit; MultiXactState->multiWrapLimit = multiWrapLimit; + MultiXactState->oldestOffset = oldestOffset; curMulti = MultiXactState->nextMXact; + nextOffset = MultiXactState->nextOffset; LWLockRelease(MultiXactGenLock); /* Log the info */ @@ -2217,7 +2249,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) * database, it'll call here, and we'll signal the postmaster to start * another iteration immediately if there are still any old databases. */ - if (MultiXactIdPrecedes(multiVacLimit, curMulti) && + if ((MultiXactIdPrecedes(multiVacLimit, curMulti) || + (nextOffset - oldestOffset > MULTIXACT_MEMBER_SAFE_THRESHOLD)) && IsUnderPostmaster && !InRecovery) SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); @@ -2594,9 +2627,9 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members) nextOffset = MultiXactState->nextOffset; oldestMultiXactId = MultiXactState->oldestMultiXactId; nextMultiXactId = MultiXactState->nextMXact; + oldestOffset = MultiXactState->oldestOffset; LWLockRelease(MultiXactGenLock); - oldestOffset = find_multixact_start(oldestMultiXactId); *members = nextOffset - oldestOffset; *multixacts = nextMultiXactId - oldestMultiXactId; }