From f692283f4f8bd307830bffae7c826e10af113074 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Wed, 15 Jul 2020 15:35:07 -0700 Subject: [PATCH v11 4/6] snapshot scalability: Move PGXACT->vacuumFlags to ProcGlobal->vacuumFlags. Similar to the previous commit this increases the chance that data frequently needed by GetSnapshotData() stays in l2 cache. As we now take care to not unnecessarily write to ProcGlobal->vacuumFlags, there should be very few modifications to the ProcGlobal->vacuumFlags array. Author: Andres Freund Reviewed-By: Robert Haas, Thomas Munro, David Rowley Discussion: https://postgr.es/m/20200301083601.ews6hz5dduc3w2se@alap3.anarazel.de --- src/include/storage/proc.h | 12 ++++- src/backend/access/transam/twophase.c | 2 +- src/backend/commands/analyze.c | 10 ++-- src/backend/commands/vacuum.c | 5 +- src/backend/postmaster/autovacuum.c | 6 +-- src/backend/replication/logical/logical.c | 3 +- src/backend/replication/slot.c | 3 +- src/backend/storage/ipc/procarray.c | 66 ++++++++++++++--------- src/backend/storage/lmgr/deadlock.c | 4 +- src/backend/storage/lmgr/proc.c | 16 +++--- 10 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index b828cecd185..ffb775939ed 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -41,7 +41,7 @@ struct XidCache }; /* - * Flags for PGXACT->vacuumFlags + * Flags for ProcGlobal->vacuumFlags[] */ #define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */ #define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */ @@ -168,6 +168,9 @@ struct PGPROC bool delayChkpt; /* true if this proc delays checkpoint start */ + uint8 vacuumFlags; /* this backend's vacuum flags, see PROC_* + * above. mirrored in + * ProcGlobal->vacuumFlags[pgxactoff] */ /* * Info to allow us to wait for synchronous replication, if needed. * waitLSN is InvalidXLogRecPtr if not waiting; set only by user backend. @@ -245,7 +248,6 @@ extern PGDLLIMPORT struct PGXACT *MyPgXact; */ typedef struct PGXACT { - uint8 vacuumFlags; /* vacuum-related flags, see above */ bool overflowed; uint8 nxids; @@ -315,6 +317,12 @@ typedef struct PROC_HDR /* Array mirroring PGPROC.xid for each PGPROC currently in the procarray */ TransactionId *xids; + /* + * Array mirroring PGPROC.vacuumFlags for each PGPROC currently in the + * procarray. + */ + uint8 *vacuumFlags; + /* Length of allProcs array */ uint32 allProcCount; /* Head of list of free PGPROC structures */ diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index d073eb07d23..3371ebd8896 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -466,7 +466,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid, proc->xid = xid; Assert(proc->xmin == InvalidTransactionId); proc->delayChkpt = false; - pgxact->vacuumFlags = 0; + proc->vacuumFlags = 0; proc->pid = 0; proc->backendId = InvalidBackendId; proc->databaseId = databaseid; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 34b71b6c1c5..2c1b956b76b 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -250,7 +250,8 @@ analyze_rel(Oid relid, RangeVar *relation, * OK, let's do it. First let other backends know I'm in ANALYZE. */ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); - MyPgXact->vacuumFlags |= PROC_IN_ANALYZE; + MyProc->vacuumFlags |= PROC_IN_ANALYZE; + ProcGlobal->vacuumFlags[MyProc->pgxactoff] = MyProc->vacuumFlags; LWLockRelease(ProcArrayLock); pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE, RelationGetRelid(onerel)); @@ -281,11 +282,12 @@ analyze_rel(Oid relid, RangeVar *relation, pgstat_progress_end_command(); /* - * Reset my PGXACT flag. Note: we need this here, and not in vacuum_rel, - * because the vacuum flag is cleared by the end-of-xact code. + * Reset vacuumFlags we set early. Note: we need this here, and not in + * vacuum_rel, because the vacuum flag is cleared by the end-of-xact code. */ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); - MyPgXact->vacuumFlags &= ~PROC_IN_ANALYZE; + MyProc->vacuumFlags &= ~PROC_IN_ANALYZE; + ProcGlobal->vacuumFlags[MyProc->pgxactoff] = MyProc->vacuumFlags; LWLockRelease(ProcArrayLock); } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 648e12c78d8..aba13c31d1b 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -1728,9 +1728,10 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) * might appear to go backwards, which is probably Not Good. */ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); - MyPgXact->vacuumFlags |= PROC_IN_VACUUM; + MyProc->vacuumFlags |= PROC_IN_VACUUM; if (params->is_wraparound) - MyPgXact->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND; + MyProc->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND; + ProcGlobal->vacuumFlags[MyProc->pgxactoff] = MyProc->vacuumFlags; LWLockRelease(ProcArrayLock); } diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index ac97e28be19..c6ec657a936 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2493,7 +2493,7 @@ do_autovacuum(void) tab->at_datname, tab->at_nspname, tab->at_relname); EmitErrorReport(); - /* this resets the PGXACT flags too */ + /* this resets ProcGlobal->vacuumFlags[i] too */ AbortOutOfAnyTransaction(); FlushErrorState(); MemoryContextResetAndDeleteChildren(PortalContext); @@ -2509,7 +2509,7 @@ do_autovacuum(void) did_vacuum = true; - /* the PGXACT flags are reset at the next end of transaction */ + /* ProcGlobal->vacuumFlags[i] are reset at the next end of xact */ /* be tidy */ deleted: @@ -2686,7 +2686,7 @@ perform_work_item(AutoVacuumWorkItem *workitem) cur_datname, cur_nspname, cur_relname); EmitErrorReport(); - /* this resets the PGXACT flags too */ + /* this resets ProcGlobal->vacuumFlags[i] too */ AbortOutOfAnyTransaction(); FlushErrorState(); MemoryContextResetAndDeleteChildren(PortalContext); diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c index 61902be3b0e..b416562ee2a 100644 --- a/src/backend/replication/logical/logical.c +++ b/src/backend/replication/logical/logical.c @@ -163,7 +163,8 @@ StartupDecodingContext(List *output_plugin_options, if (!IsTransactionOrTransactionBlock()) { LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); - MyPgXact->vacuumFlags |= PROC_IN_LOGICAL_DECODING; + MyProc->vacuumFlags |= PROC_IN_LOGICAL_DECODING; + ProcGlobal->vacuumFlags[MyProc->pgxactoff] = MyProc->vacuumFlags; LWLockRelease(ProcArrayLock); } diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 57bbb6288c6..ca46256f9d0 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -520,7 +520,8 @@ ReplicationSlotRelease(void) /* might not have been set when we've been a plain slot */ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); - MyPgXact->vacuumFlags &= ~PROC_IN_LOGICAL_DECODING; + MyProc->vacuumFlags &= ~PROC_IN_LOGICAL_DECODING; + ProcGlobal->vacuumFlags[MyProc->pgxactoff] = MyProc->vacuumFlags; LWLockRelease(ProcArrayLock); } diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index a9b32565367..e72a0705abb 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -474,9 +474,12 @@ ProcArrayAdd(PGPROC *proc) (arrayP->numProcs - index) * sizeof(*arrayP->pgprocnos)); memmove(&ProcGlobal->xids[index + 1], &ProcGlobal->xids[index], (arrayP->numProcs - index) * sizeof(*ProcGlobal->xids)); + memmove(&ProcGlobal->vacuumFlags[index + 1], &ProcGlobal->vacuumFlags[index], + (arrayP->numProcs - index) * sizeof(*ProcGlobal->vacuumFlags)); arrayP->pgprocnos[index] = proc->pgprocno; ProcGlobal->xids[index] = proc->xid; + ProcGlobal->vacuumFlags[index] = proc->vacuumFlags; arrayP->numProcs++; @@ -537,6 +540,7 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid) } Assert(TransactionIdIsValid(ProcGlobal->xids[proc->pgxactoff] == 0)); + ProcGlobal->vacuumFlags[proc->pgxactoff] = 0; for (index = 0; index < arrayP->numProcs; index++) { @@ -547,6 +551,8 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid) (arrayP->numProcs - index - 1) * sizeof(*arrayP->pgprocnos)); memmove(&ProcGlobal->xids[index], &ProcGlobal->xids[index + 1], (arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->xids)); + memmove(&ProcGlobal->vacuumFlags[index], &ProcGlobal->vacuumFlags[index + 1], + (arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->vacuumFlags)); arrayP->pgprocnos[arrayP->numProcs - 1] = -1; /* for debugging */ arrayP->numProcs--; @@ -625,14 +631,24 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid) Assert(!TransactionIdIsValid(proc->xid)); proc->lxid = InvalidLocalTransactionId; - /* must be cleared with xid/xmin: */ - pgxact->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; proc->xmin = InvalidTransactionId; proc->delayChkpt = false; /* be sure this is cleared in abort */ proc->recoveryConflictPending = false; Assert(pgxact->nxids == 0); Assert(pgxact->overflowed == false); + + /* must be cleared with xid/xmin: */ + /* avoid unnecessarily dirtying shared cachelines */ + if (proc->vacuumFlags & PROC_VACUUM_STATE_MASK) + { + Assert(!LWLockHeldByMe(ProcArrayLock)); + LWLockAcquire(ProcArrayLock, LW_SHARED); + Assert(proc->vacuumFlags == ProcGlobal->vacuumFlags[proc->pgxactoff]); + proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; + ProcGlobal->vacuumFlags[proc->pgxactoff] = proc->vacuumFlags; + LWLockRelease(ProcArrayLock); + } } } @@ -653,12 +669,18 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact, ProcGlobal->xids[pgxactoff] = InvalidTransactionId; proc->xid = InvalidTransactionId; proc->lxid = InvalidLocalTransactionId; - /* must be cleared with xid/xmin: */ - pgxact->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; proc->xmin = InvalidTransactionId; proc->delayChkpt = false; /* be sure this is cleared in abort */ proc->recoveryConflictPending = false; + /* must be cleared with xid/xmin: */ + /* avoid unnecessarily dirtying shared cachelines */ + if (proc->vacuumFlags & PROC_VACUUM_STATE_MASK) + { + proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; + ProcGlobal->vacuumFlags[proc->pgxactoff] = proc->vacuumFlags; + } + /* Clear the subtransaction-XID cache too while holding the lock */ pgxact->nxids = 0; pgxact->overflowed = false; @@ -818,9 +840,8 @@ ProcArrayClearTransaction(PGPROC *proc) proc->xmin = InvalidTransactionId; proc->recoveryConflictPending = false; - /* redundant, but just in case */ - pgxact->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; - proc->delayChkpt = false; + Assert(!(proc->vacuumFlags & PROC_VACUUM_STATE_MASK)); + Assert(!proc->delayChkpt); /* Clear the subtransaction-XID cache too */ pgxact->nxids = 0; @@ -1611,7 +1632,7 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h) { int pgprocno = arrayP->pgprocnos[index]; PGPROC *proc = &allProcs[pgprocno]; - PGXACT *pgxact = &allPgXact[pgprocno]; + int8 vacuumFlags = ProcGlobal->vacuumFlags[index]; TransactionId xid; TransactionId xmin; @@ -1628,10 +1649,6 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h) */ xmin = TransactionIdOlder(xmin, xid); - /* if neither is set, this proc doesn't influence the horizon */ - if (!TransactionIdIsValid(xmin)) - continue; - /* * Don't ignore any procs when determining which transactions might be * considered running. While slots should ensure logical decoding @@ -1646,7 +1663,7 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h) * removed, as long as pg_subtrans is not truncated) or doing logical * decoding (which manages xmin separately, check below). */ - if (pgxact->vacuumFlags & (PROC_IN_VACUUM | PROC_IN_LOGICAL_DECODING)) + if (vacuumFlags & (PROC_IN_VACUUM | PROC_IN_LOGICAL_DECODING)) continue; /* shared tables need to take backends in all database into account */ @@ -1984,6 +2001,7 @@ GetSnapshotData(Snapshot snapshot) size_t numProcs = arrayP->numProcs; TransactionId *xip = snapshot->xip; int *pgprocnos = arrayP->pgprocnos; + uint8 *allVacuumFlags = ProcGlobal->vacuumFlags; /* * First collect set of pgxactoff/xids that need to be included in the @@ -1993,8 +2011,6 @@ GetSnapshotData(Snapshot snapshot) { /* Fetch xid just once - see GetNewTransactionId */ TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]); - int pgprocno; - PGXACT *pgxact; uint8 vacuumFlags; Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff); @@ -2030,14 +2046,11 @@ GetSnapshotData(Snapshot snapshot) if (!NormalTransactionIdPrecedes(xid, xmax)) continue; - pgprocno = pgprocnos[pgxactoff]; - pgxact = &allPgXact[pgprocno]; - vacuumFlags = pgxact->vacuumFlags; - /* * Skip over backends doing logical decoding which manages xmin * separately (check below) and ones running LAZY VACUUM. */ + vacuumFlags = allVacuumFlags[pgxactoff]; if (vacuumFlags & (PROC_IN_LOGICAL_DECODING | PROC_IN_VACUUM)) continue; @@ -2064,6 +2077,9 @@ GetSnapshotData(Snapshot snapshot) */ if (!suboverflowed) { + int pgprocno = pgprocnos[pgxactoff]; + PGXACT *pgxact = &allPgXact[pgprocno]; + if (pgxact->overflowed) suboverflowed = true; else @@ -2282,11 +2298,11 @@ ProcArrayInstallImportedXmin(TransactionId xmin, { int pgprocno = arrayP->pgprocnos[index]; PGPROC *proc = &allProcs[pgprocno]; - PGXACT *pgxact = &allPgXact[pgprocno]; + int vacuumFlags = ProcGlobal->vacuumFlags[index]; TransactionId xid; /* Ignore procs running LAZY VACUUM */ - if (pgxact->vacuumFlags & PROC_IN_VACUUM) + if (vacuumFlags & PROC_IN_VACUUM) continue; /* We are only interested in the specific virtual transaction. */ @@ -2975,12 +2991,12 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, { int pgprocno = arrayP->pgprocnos[index]; PGPROC *proc = &allProcs[pgprocno]; - PGXACT *pgxact = &allPgXact[pgprocno]; + uint8 vacuumFlags = ProcGlobal->vacuumFlags[index]; if (proc == MyProc) continue; - if (excludeVacuum & pgxact->vacuumFlags) + if (excludeVacuum & vacuumFlags) continue; if (allDbs || proc->databaseId == MyDatabaseId) @@ -3395,7 +3411,7 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared) { int pgprocno = arrayP->pgprocnos[index]; PGPROC *proc = &allProcs[pgprocno]; - PGXACT *pgxact = &allPgXact[pgprocno]; + uint8 vacuumFlags = ProcGlobal->vacuumFlags[index]; if (proc->databaseId != databaseId) continue; @@ -3409,7 +3425,7 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared) else { (*nbackends)++; - if ((pgxact->vacuumFlags & PROC_IS_AUTOVACUUM) && + if ((vacuumFlags & PROC_IS_AUTOVACUUM) && nautovacs < MAXAUTOVACPIDS) autovac_pids[nautovacs++] = proc->pid; } diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c index beedc7947db..e1246b8a4da 100644 --- a/src/backend/storage/lmgr/deadlock.c +++ b/src/backend/storage/lmgr/deadlock.c @@ -544,7 +544,6 @@ FindLockCycleRecurseMember(PGPROC *checkProc, { PGPROC *proc; LOCK *lock = checkProc->waitLock; - PGXACT *pgxact; PROCLOCK *proclock; SHM_QUEUE *procLocks; LockMethod lockMethodTable; @@ -582,7 +581,6 @@ FindLockCycleRecurseMember(PGPROC *checkProc, PGPROC *leader; proc = proclock->tag.myProc; - pgxact = &ProcGlobal->allPgXact[proc->pgprocno]; leader = proc->lockGroupLeader == NULL ? proc : proc->lockGroupLeader; /* A proc never blocks itself or any other lock group member */ @@ -630,7 +628,7 @@ FindLockCycleRecurseMember(PGPROC *checkProc, * ProcArrayLock. */ if (checkProc == MyProc && - pgxact->vacuumFlags & PROC_IS_AUTOVACUUM) + proc->vacuumFlags & PROC_IS_AUTOVACUUM) blocking_autovacuum_proc = proc; /* We're done looking at this proclock */ diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 7fad49544ce..f6113b2d243 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -114,6 +114,7 @@ ProcGlobalShmemSize(void) size = add_size(size, mul_size(NUM_AUXILIARY_PROCS, sizeof(PGXACT))); size = add_size(size, mul_size(max_prepared_xacts, sizeof(PGXACT))); size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids))); + size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->vacuumFlags))); return size; } @@ -223,6 +224,8 @@ InitProcGlobal(void) ProcGlobal->xids = (TransactionId *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->xids)); MemSet(ProcGlobal->xids, 0, TotalProcs * sizeof(*ProcGlobal->xids)); + ProcGlobal->vacuumFlags = (uint8 *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->vacuumFlags)); + MemSet(ProcGlobal->vacuumFlags, 0, TotalProcs * sizeof(*ProcGlobal->vacuumFlags)); for (i = 0; i < TotalProcs; i++) { @@ -405,10 +408,10 @@ InitProcess(void) MyProc->tempNamespaceId = InvalidOid; MyProc->isBackgroundWorker = IsBackgroundWorker; MyProc->delayChkpt = false; - MyPgXact->vacuumFlags = 0; + MyProc->vacuumFlags = 0; /* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */ if (IsAutoVacuumWorkerProcess()) - MyPgXact->vacuumFlags |= PROC_IS_AUTOVACUUM; + MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM; MyProc->lwWaiting = false; MyProc->lwWaitMode = 0; MyProc->waitLock = NULL; @@ -587,7 +590,7 @@ InitAuxiliaryProcess(void) MyProc->tempNamespaceId = InvalidOid; MyProc->isBackgroundWorker = IsBackgroundWorker; MyProc->delayChkpt = false; - MyPgXact->vacuumFlags = 0; + MyProc->vacuumFlags = 0; MyProc->lwWaiting = false; MyProc->lwWaitMode = 0; MyProc->waitLock = NULL; @@ -1323,7 +1326,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable) if (deadlock_state == DS_BLOCKED_BY_AUTOVACUUM && allow_autovacuum_cancel) { PGPROC *autovac = GetBlockingAutoVacuumPgproc(); - PGXACT *autovac_pgxact = &ProcGlobal->allPgXact[autovac->pgprocno]; + uint8 vacuumFlags; LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); @@ -1331,8 +1334,9 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable) * Only do it if the worker is not working to protect against Xid * wraparound. */ - if ((autovac_pgxact->vacuumFlags & PROC_IS_AUTOVACUUM) && - !(autovac_pgxact->vacuumFlags & PROC_VACUUM_FOR_WRAPAROUND)) + vacuumFlags = ProcGlobal->vacuumFlags[proc->pgxactoff]; + if ((vacuumFlags & PROC_IS_AUTOVACUUM) && + !(vacuumFlags & PROC_VACUUM_FOR_WRAPAROUND)) { int pid = autovac->pid; StringInfoData locktagbuf; -- 2.25.0.114.g5b0ca878e0