diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 90fa030caf..ca702eec81 100644 --- a/src/backend/access/transam/multixact.c +++ b/src/backend/access/transam/multixact.c @@ -1768,14 +1768,37 @@ MaybeExtendOffsetSlru(void) * * 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; @@ -1785,7 +1808,9 @@ StartupMultiXact(void) /* * 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(); @@ -1794,7 +1819,7 @@ StartupMultiXact(void) 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; @@ -1824,7 +1849,7 @@ StartupMultiXact(void) 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; @@ -2258,9 +2283,15 @@ SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data) * 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) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index de19d2240f..b68230d196 100755 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6332,6 +6332,14 @@ StartupXLOG(void) 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 @@ -6532,8 +6540,9 @@ StartupXLOG(void) 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); @@ -7197,8 +7206,8 @@ StartupXLOG(void) /* * Perform end of recovery actions for any SLRUs that need it. */ - StartupMultiXact(); TrimCLOG(); + TrimMultiXact(); /* Reload shared-memory state for prepared transactions */ RecoverPreparedTransactions(); @@ -8619,6 +8628,21 @@ CreateRestartPoint(int flags) } 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 diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h index e6db81a827..6085ea3ec1 100644 --- a/src/include/access/multixact.h +++ b/src/include/access/multixact.h @@ -98,6 +98,7 @@ extern Size MultiXactShmemSize(void); extern void MultiXactShmemInit(void); extern void BootStrapMultiXact(void); extern void StartupMultiXact(void); +extern void TrimMultiXact(void); extern void ShutdownMultiXact(void); extern void SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid);