Commit 04e6d3b8 authored by Robert Haas's avatar Robert Haas

Even when autovacuum=off, force it for members as we do in other cases.

Thomas Munro, with some adjustments by me.
parent f6a6c46d
...@@ -204,6 +204,7 @@ typedef struct MultiXactStateData ...@@ -204,6 +204,7 @@ typedef struct MultiXactStateData
*/ */
MultiXactId oldestMultiXactId; MultiXactId oldestMultiXactId;
Oid oldestMultiXactDB; Oid oldestMultiXactDB;
MultiXactOffset oldestOffset;
/* /*
* This is what the previous checkpoint stored as the truncate position. * This is what the previous checkpoint stored as the truncate position.
...@@ -954,14 +955,17 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset) ...@@ -954,14 +955,17 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
* against catastrophic data loss due to multixact wraparound. The basic * against catastrophic data loss due to multixact wraparound. The basic
* rules are: * 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 multiWarnLimit, start issuing warnings.
* If we're past multiStopLimit, refuse to create new MultiXactIds. * If we're past multiStopLimit, refuse to create new MultiXactIds.
* *
* Note these are pretty much the same protections in GetNewTransactionId. * 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 * For safety's sake, we release MultiXactGenLock while sending
...@@ -2142,6 +2146,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) ...@@ -2142,6 +2146,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
MultiXactId multiStopLimit; MultiXactId multiStopLimit;
MultiXactId multiWrapLimit; MultiXactId multiWrapLimit;
MultiXactId curMulti; MultiXactId curMulti;
MultiXactOffset oldestOffset;
MultiXactOffset nextOffset;
Assert(MultiXactIdIsValid(oldest_datminmxid)); Assert(MultiXactIdIsValid(oldest_datminmxid));
...@@ -2194,6 +2200,35 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) ...@@ -2194,6 +2200,35 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
if (multiVacLimit < FirstMultiXactId) if (multiVacLimit < FirstMultiXactId)
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 */ /* Grab lock for just long enough to set the new limit values */
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
MultiXactState->oldestMultiXactId = oldest_datminmxid; MultiXactState->oldestMultiXactId = oldest_datminmxid;
...@@ -2202,7 +2237,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) ...@@ -2202,7 +2237,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
MultiXactState->multiWarnLimit = multiWarnLimit; MultiXactState->multiWarnLimit = multiWarnLimit;
MultiXactState->multiStopLimit = multiStopLimit; MultiXactState->multiStopLimit = multiStopLimit;
MultiXactState->multiWrapLimit = multiWrapLimit; MultiXactState->multiWrapLimit = multiWrapLimit;
MultiXactState->oldestOffset = oldestOffset;
curMulti = MultiXactState->nextMXact; curMulti = MultiXactState->nextMXact;
nextOffset = MultiXactState->nextOffset;
LWLockRelease(MultiXactGenLock); LWLockRelease(MultiXactGenLock);
/* Log the info */ /* Log the info */
...@@ -2217,7 +2254,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) ...@@ -2217,7 +2254,8 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
* database, it'll call here, and we'll signal the postmaster to start * database, it'll call here, and we'll signal the postmaster to start
* another iteration immediately if there are still any old databases. * 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) IsUnderPostmaster && !InRecovery)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
...@@ -2476,11 +2514,16 @@ DetermineSafeOldestOffset(MultiXactId oldestMXact) ...@@ -2476,11 +2514,16 @@ DetermineSafeOldestOffset(MultiXactId oldestMXact)
MultiXactOffset oldestOffset; MultiXactOffset oldestOffset;
/* /*
* We determine the safe upper bound for offsets of new xacts by reading * Determine the offset of the oldest multixact. Normally, we can read
* the offset of the oldest multixact, and going back one segment. This * the offset from the multixact itself, but there's an important special
* way, the sequence of multixact member segments will always have a * case: if there are no multixacts in existence at all, oldestMXact
* one-segment hole at a minimum. We start spewing warnings a few * obviously can't point to one. It will instead point to the multixact
* complete segments before that. * 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); LWLockAcquire(MultiXactGenLock, LW_SHARED);
if (MultiXactState->nextMXact == oldestMXact) if (MultiXactState->nextMXact == oldestMXact)
...@@ -2594,9 +2637,9 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members) ...@@ -2594,9 +2637,9 @@ ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
nextOffset = MultiXactState->nextOffset; nextOffset = MultiXactState->nextOffset;
oldestMultiXactId = MultiXactState->oldestMultiXactId; oldestMultiXactId = MultiXactState->oldestMultiXactId;
nextMultiXactId = MultiXactState->nextMXact; nextMultiXactId = MultiXactState->nextMXact;
oldestOffset = MultiXactState->oldestOffset;
LWLockRelease(MultiXactGenLock); LWLockRelease(MultiXactGenLock);
oldestOffset = find_multixact_start(oldestMultiXactId);
*members = nextOffset - oldestOffset; *members = nextOffset - oldestOffset;
*multixacts = nextMultiXactId - oldestMultiXactId; *multixacts = nextMultiXactId - oldestMultiXactId;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment