* Note these are pretty much the same protections in GetNewTransactionId.
*----------
*/
- if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit) ||
- !MultiXactState->oldestOffsetKnown ||
- (MultiXactState->nextOffset - MultiXactState->oldestOffset
- > MULTIXACT_MEMBER_SAFE_THRESHOLD))
+ if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit))
{
/*
* For safety's sake, we release MultiXactGenLock while sending
LWLockRelease(MultiXactGenLock);
- /*
- * To avoid swamping the postmaster with signals, we issue the autovac
- * request only once per 64K multis generated. This still gives
- * plenty of chances before we get into real trouble.
- */
- if (IsUnderPostmaster && (result % 65536) == 0)
- SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
-
if (IsUnderPostmaster &&
!MultiXactIdPrecedes(result, multiStopLimit))
{
char *oldest_datname = get_database_name(oldest_datoid);
+ /*
+ * Immediately kick autovacuum into action as we're already
+ * in ERROR territory.
+ */
+ SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
+
/* complain even if that DB has disappeared */
if (oldest_datname)
ereport(ERROR,
errhint("Execute a database-wide VACUUM in that database.\n"
"You might also need to commit or roll back old prepared transactions.")));
}
- else if (!MultiXactIdPrecedes(result, multiWarnLimit))
+
+ /*
+ * To avoid swamping the postmaster with signals, we issue the autovac
+ * request only once per 64K multis generated. This still gives
+ * plenty of chances before we get into real trouble.
+ */
+ if (IsUnderPostmaster && (result % 65536) == 0)
+ SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
+
+ if (!MultiXactIdPrecedes(result, multiWarnLimit))
{
char *oldest_datname = get_database_name(oldest_datoid);
if (MultiXactState->offsetStopLimitKnown &&
MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit, nextOffset,
nmembers))
+ {
+ /* see comment in the corresponding offsets wraparound case */
+ SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
+
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("multixact \"members\" limit exceeded"),
MultiXactState->offsetStopLimit - nextOffset - 1),
errhint("Execute a database-wide VACUUM in database with OID %u with reduced vacuum_multixact_freeze_min_age and vacuum_multixact_freeze_table_age settings.",
MultiXactState->oldestMultiXactDB)));
- else if (MultiXactState->offsetStopLimitKnown &&
- MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit,
- nextOffset,
- nmembers + MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
+ }
+
+ /*
+ * Check whether we should kick autovacuum into action, to prevent members
+ * wraparound. NB we use a much larger window to trigger autovacuum than
+ * just the warning limit. The warning is just a measure of last resort -
+ * this is in line with GetNewTransactionId's behaviour.
+ */
+ if (!MultiXactState->oldestOffsetKnown ||
+ (MultiXactState->nextOffset - MultiXactState->oldestOffset
+ > MULTIXACT_MEMBER_SAFE_THRESHOLD))
+ {
+ /*
+ * To avoid swamping the postmaster with signals, we issue the autovac
+ * request only when crossing a segment boundary. With default
+ * compilation settings that's rougly after 50k members. This still
+ * gives plenty of chances before we get into real trouble.
+ */
+ if ((MXOffsetToMemberPage(nextOffset) / SLRU_PAGES_PER_SEGMENT) !=
+ (MXOffsetToMemberPage(nextOffset + nmembers) / SLRU_PAGES_PER_SEGMENT))
+ SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
+ }
+
+ if (MultiXactState->offsetStopLimitKnown &&
+ MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit,
+ nextOffset,
+ nmembers + MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
ereport(WARNING,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("database with OID %u must be vacuumed before %d more multixact members are used",