|
|
|
|
@ -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,35 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) |
|
|
|
|
if (multiVacLimit < FirstMultiXactId) |
|
|
|
|
multiVacLimit += FirstMultiXactId; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Determine the offset of the oldest multixact that might still be |
|
|
|
|
* referenced. Normally, we can read the offset from the multixact itself, |
|
|
|
|
* but there's an important special case: if there are no multixacts in |
|
|
|
|
* existence at all, oldest_datminmxid obviously can't point to one. It |
|
|
|
|
* will instead point to the multixact ID that will be assigned the next |
|
|
|
|
* time one is needed. |
|
|
|
|
* |
|
|
|
|
* NB: oldest_dataminmxid is the oldest multixact that might still be |
|
|
|
|
* referenced from a table, unlike in DetermineSafeOldestOffset, where we |
|
|
|
|
* do this same computation based on the oldest value that might still |
|
|
|
|
* exist in the SLRU. This is because here we're trying to compute a |
|
|
|
|
* threshold for activating autovacuum, which can only remove references |
|
|
|
|
* to multixacts, whereas there we are computing a threshold for creating |
|
|
|
|
* new multixacts, which requires the old ones to have first been |
|
|
|
|
* truncated away by a checkpoint. |
|
|
|
|
*/ |
|
|
|
|
LWLockAcquire(MultiXactGenLock, LW_SHARED); |
|
|
|
|
if (MultiXactState->nextMXact == oldest_datminmxid) |
|
|
|
|
{ |
|
|
|
|
oldestOffset = MultiXactState->nextOffset; |
|
|
|
|
LWLockRelease(MultiXactGenLock); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
LWLockRelease(MultiXactGenLock); |
|
|
|
|
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 +2237,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 +2254,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); |
|
|
|
|
|
|
|
|
|
@ -2476,11 +2514,16 @@ DetermineSafeOldestOffset(MultiXactId oldestMXact) |
|
|
|
|
MultiXactOffset oldestOffset; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We determine the safe upper bound for offsets of new xacts by reading |
|
|
|
|
* the offset of the oldest multixact, and going back one segment. This |
|
|
|
|
* way, the sequence of multixact member segments will always have a |
|
|
|
|
* one-segment hole at a minimum. We start spewing warnings a few |
|
|
|
|
* complete segments before that. |
|
|
|
|
* Determine the offset of the oldest multixact. Normally, we can read |
|
|
|
|
* the offset from the multixact itself, but there's an important special |
|
|
|
|
* case: if there are no multixacts in existence at all, oldestMXact |
|
|
|
|
* obviously can't point to one. It will instead point to the multixact |
|
|
|
|
* ID that will be assigned the next time one is needed. |
|
|
|
|
* |
|
|
|
|
* NB: oldestMXact should be the oldest multixact that still exists in |
|
|
|
|
* the SLRU, unlike in SetMultiXactIdLimit, where we do this same |
|
|
|
|
* computation based on the oldest value that might be referenced in a |
|
|
|
|
* table. |
|
|
|
|
*/ |
|
|
|
|
LWLockAcquire(MultiXactGenLock, LW_SHARED); |
|
|
|
|
if (MultiXactState->nextMXact == oldestMXact) |
|
|
|
|
@ -2594,9 +2637,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; |
|
|
|
|
} |
|
|
|
|
|