snapshot scalability: Move PGXACT->vacuumFlags to ProcGlobal->vacuumFlags.
authorAndres Freund <[email protected]>
Wed, 15 Jul 2020 22:35:07 +0000 (15:35 -0700)
committerAndres Freund <[email protected]>
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 <[email protected]>
Reviewed-By: Robert Haas <[email protected]>
Reviewed-By: Thomas Munro <[email protected]>
Reviewed-By: David Rowley <[email protected]>
Discussion: https://postgr.es/m/20200301083601[email protected]

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 */