From 59a1af7d77dd63deac48270f36e5419244e38c80 Mon Sep 17 00:00:00 2001
From: Andres Freund
Date: Wed, 15 Jul 2020 15:35:07 -0700
Subject: [PATCH v13 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/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 +++---
9 files changed, 73 insertions(+), 44 deletions(-)
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 146bca84bd6..ea95cf92402 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 */
@@ -167,6 +167,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.
@@ -244,7 +247,6 @@ extern PGDLLIMPORT struct PGXACT *MyPgXact;
*/
typedef struct PGXACT
{
- uint8 vacuumFlags; /* vacuum-related flags, see above */
bool overflowed;
uint8 nxids;
@@ -314,6 +316,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 a0398bf3a3e..744b8a7f393 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/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 57c5b513ccf..0f6af952f93 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -181,7 +181,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 3dc01b6df22..42c78eabd4e 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 e5617ddfb41..e77d4e44fb8 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -476,9 +476,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++;
@@ -539,6 +542,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++)
{
@@ -549,6 +553,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--;
@@ -627,14 +633,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);
+ }
}
}
@@ -655,12 +671,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;
@@ -820,9 +842,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;
@@ -1624,7 +1645,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;
@@ -1641,10 +1662,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
@@ -1659,7 +1676,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 */
@@ -2000,6 +2017,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
@@ -2009,8 +2027,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);
@@ -2046,14 +2062,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;
@@ -2080,6 +2093,9 @@ GetSnapshotData(Snapshot snapshot)
*/
if (!suboverflowed)
{
+ int pgprocno = pgprocnos[pgxactoff];
+ PGXACT *pgxact = &allPgXact[pgprocno];
+
if (pgxact->overflowed)
suboverflowed = true;
else
@@ -2298,11 +2314,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. */
@@ -2992,12 +3008,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)
@@ -3412,7 +3428,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;
@@ -3426,7 +3442,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