*
* StartupXLOG has already established nextMXact/nextOffset by calling
* MultiXactSetNextMXact and/or MultiXactAdvanceNextMXact, and the oldestMulti
- * info from pg_control and/or MultiXactAdvanceOldest. Note that we may
- * already have replayed WAL data into the SLRU files.
- *
- * We don't need any locks here, really; the SLRU locks are taken
- * only because slru.c expects to be called with locks held.
+ * info from pg_control and/or MultiXactAdvanceOldest, but we haven't yet
+ * replayed WAL.
*/
void
StartupMultiXact(void)
+{
+ MultiXactId multi = MultiXactState->nextMXact;
+ MultiXactOffset offset = MultiXactState->nextOffset;
+ int pageno;
+
+ /*
+ * Initialize offset's idea of the latest page number.
+ */
+ pageno = MultiXactIdToOffsetPage(multi);
+ MultiXactOffsetCtl->shared->latest_page_number = pageno;
+
+ /*
+ * Initialize member's idea of the latest page number.
+ */
+ pageno = MXOffsetToMemberPage(offset);
+ MultiXactMemberCtl->shared->latest_page_number = pageno;
+}
+
+/*
+ * This must be called ONCE at the end of startup/recovery.
+ *
+ * We don't need any locks here, really; the SLRU locks are taken only because
+ * slru.c expects to be called with locks held.
+ */
+void
+TrimMultiXact(void)
{
MultiXactId multi = MultiXactState->nextMXact;
MultiXactOffset offset = MultiXactState->nextOffset;
/*
* During a binary upgrade, make sure that the offsets SLRU is large
- * enough to contain the next value that would be created.
+ * enough to contain the next value that would be created. It's fine to do
+ * this here and not in StartupMultiXact() since binary upgrades should
+ * never need crash recovery.
*/
if (IsBinaryUpgrade)
MaybeExtendOffsetSlru();
LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
/*
- * Initialize our idea of the latest page number.
+ * (Re-)Initialize our idea of the latest page number.
*/
pageno = MultiXactIdToOffsetPage(multi);
MultiXactOffsetCtl->shared->latest_page_number = pageno;
LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
/*
- * Initialize our idea of the latest page number.
+ * (Re-)Initialize our idea of the latest page number.
*/
pageno = MXOffsetToMemberPage(offset);
MultiXactMemberCtl->shared->latest_page_number = pageno;
* Remove all MultiXactOffset and MultiXactMember segments before the oldest
* ones still of interest.
*
- * This is called by vacuum after it has successfully advanced a database's
- * datminmxid value; the cutoff value we're passed is the minimum of all
- * databases' datminmxid values.
+ * On a primary, this is called by vacuum after it has successfully advanced a
+ * database's datminmxid value; the cutoff value we're passed is the minimum of
+ * all databases' datminmxid values.
+ *
+ * During crash recovery, it's called from CreateRestartPoint() instead. We
+ * rely on the fact that xlog_redo() will already have called
+ * MultiXactAdvanceOldest(). Our latest_page_number will already have been
+ * initialized by StartupMultiXact() and kept up to date as new pages are
+ * zeroed.
*/
void
TruncateMultiXact(MultiXactId oldestMXact)
XLogCtl->ckptXidEpoch = checkPoint.nextXidEpoch;
XLogCtl->ckptXid = checkPoint.nextXid;
+ /*
+ * Startup MultiXact. We need to do this early for two reasons: one
+ * is that we might try to access multixacts when we do tuple freezing,
+ * and the other is we need its state initialized because we attempt
+ * truncation during restartpoints.
+ */
+ StartupMultiXact();
+
/*
* Initialize unlogged LSN. On a clean shutdown, it's restored from the
* control file. On recovery, all unlogged relations are blown away, so
ProcArrayInitRecovery(ShmemVariableCache->nextXid);
/*
- * Startup commit log and subtrans only. Other SLRUs are not
- * maintained during recovery and need not be started yet.
+ * Startup commit log and subtrans only. MultiXact has already
+ * been started up and other SLRUs are not maintained during
+ * recovery and need not be started yet.
*/
StartupCLOG();
StartupSUBTRANS(oldestActiveXID);
/*
* Perform end of recovery actions for any SLRUs that need it.
*/
- StartupMultiXact();
TrimCLOG();
+ TrimMultiXact();
/* Reload shared-memory state for prepared transactions */
RecoverPreparedTransactions();
}
LWLockRelease(ControlFileLock);
+ /*
+ * Due to an historical accident multixact truncations are not WAL-logged,
+ * but just performed everytime the mxact horizon is increased. So, unless
+ * we explicitly execute truncations on a standby it will never clean out
+ * /pg_multixact which obviously is bad, both because it uses space and
+ * because we can wrap around into pre-existing data...
+ *
+ * We can only do the truncation here, after the UpdateControlFile()
+ * above, because we've now safely established a restart point, that
+ * guarantees we will not need need to access those multis.
+ *
+ * It's probably worth improving this.
+ */
+ TruncateMultiXact(lastCheckPoint.oldestMulti);
+
/*
* Delete old log files (those no longer needed even for previous
* checkpoint/restartpoint) to prevent the disk holding the xlog from