]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
snapshot scalability: Move PGXACT->vacuumFlags to ProcGlobal->vacuumFlags.
authorAndres Freund <andres@anarazel.de>
Wed, 15 Jul 2020 22:35:07 +0000 (15:35 -0700)
committerAndres Freund <andres@anarazel.de>
Fri, 14 Aug 2020 22:33:35 +0000 (15:33 -0700)
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 <andres@anarazel.de>
Reviewed-By: Robert Haas <robertmhaas@gmail.com>
Reviewed-By: Thomas Munro <thomas.munro@gmail.com>
Reviewed-By: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/20200301083601.ews6hz5dduc3w2se@alap3.anarazel.de

src/backend/access/transam/twophase.c
src/backend/commands/vacuum.c
src/backend/postmaster/autovacuum.c
src/backend/replication/logical/logical.c
src/backend/replication/slot.c
src/backend/storage/ipc/procarray.c
src/backend/storage/lmgr/deadlock.c
src/backend/storage/lmgr/proc.c
src/include/storage/proc.h

index a0398bf3a3e82263c7201cdf03a5daf43d8e3963..744b8a7f39352bce409a8f08d71748e84e4b15e4 100644 (file)
@@ -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;
index 648e12c78d84e48c8ed26a576403ed76cc74b311..aba13c31d1bc2e957cb7b61bd6983b4f91389740 100644 (file)
@@ -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);
        }
 
index ac97e28be19c7ce904db3ce43ce85e11c959ff3f..c6ec657a9367c0e99b9be9bd729df2c9afbebd9f 100644 (file)
@@ -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);
index 57c5b513ccf848a7a8ccb8ae4cb640edbc213035..0f6af952f93940032714fb957aaadccef2e91cb4 100644 (file)
@@ -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);
        }
 
index 3dc01b6df22a966ccbab2ef9a5d144d48c86d7d7..42c78eabd4eb9ef805991d56a18ea7a4a63742cc 100644 (file)
@@ -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);
 }
 
index 0bf20a49375d0434fdb883b1fc69c836e70e20ca..224da4f9510b8985317276d0fc611a96872d9c39 100644 (file)
@@ -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--;
@@ -626,14 +632,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);
+               }
        }
 }
 
@@ -654,12 +670,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;
@@ -819,9 +841,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;
@@ -1623,7 +1644,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;
 
@@ -1640,8 +1661,8 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
                 */
                xmin = TransactionIdOlder(xmin, xid);
 
-               /* if neither is set, this proc doesn't influence the horizon */
-               if (!TransactionIdIsValid(xmin))
+        /* if neither is set, this proc doesn't influence the horizon */
+        if (!TransactionIdIsValid(xmin))
                        continue;
 
                /*
@@ -1658,7 +1679,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 */
@@ -1998,6 +2019,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
@@ -2007,8 +2029,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);
@@ -2044,14 +2064,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;
 
@@ -2078,6 +2095,9 @@ GetSnapshotData(Snapshot snapshot)
                         */
                        if (!suboverflowed)
                        {
+                               int                     pgprocno = pgprocnos[pgxactoff];
+                               PGXACT     *pgxact = &allPgXact[pgprocno];
+
                                if (pgxact->overflowed)
                                        suboverflowed = true;
                                else
@@ -2296,11 +2316,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. */
@@ -2990,12 +3010,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)
@@ -3410,7 +3430,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;
@@ -3424,7 +3444,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;
                        }
index beedc7947db945915e712f39f33deabce35d5303..e1246b8a4da17a29ba86394618190812b9029b2f 100644 (file)
@@ -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 */
index 7fad49544ce09c7386911ed022255d64201aa69a..f6113b2d24320573b19240a1fc7af9dee936078c 100644 (file)
@@ -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;
index e29ed85e53dbba3b05dd9949eef3af856aabc2de..9f3a8b518eb2f7ab8cc8546ce6536d1dd687022b 100644 (file)
@@ -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 */