From f6b11c24bf28b3573eaab773531bf7c0a705b0c6 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 25 Jan 2024 00:49:17 +0200 Subject: [PATCH v7 2/4] Redefine backend ID to be an index into the proc array. Previously, backend ID was an index into the ProcState array, in the shared cache invalidation manager (sinvaladt.c). The entry in the ProcState array was reserved at backend startup by scanning the array for a free entry, and that was also when the backend got its backend ID. Things becomes slightly simpler if we redefine backend ID to be the index into the PGPROC array, and directly use it also as an index to the ProcState array. This uses a little more memory, as we reserve a few extra slots in the ProcState array for aux processes that don't need them, but the simplicity is worth it. Aux processes now also have a backend ID. This simplifies the reservation of BackendStatusArray and ProcSignal slots. You can now convert a backend ID into an index into the PGPROC array simply by subtracting 1. We still use 0-based "pgprocnos" in various places, for indexes into the PGPROC array, but the only difference now is that backend IDs start at 1 while pgprocnos start at 0. One potential downside of this patch is that the ProcState array might get less densely packed, as we we don't try so hard to assign low-numbered backend ID anymore. If it's less densely packed, lastBackend will stay at a higher value, and SIInsertDataEntries() and SICleanupQueue() need to scan over more unused entries. I think that's fine. They are performance critical enough to matter, and there was no guarantee on dense packing before either: If you launched a lot of backends concurrently, and kept the last one open, lastBackend would also stay at a high value. Another potential downside of not assigning low backend IDs as aggressively is that you might end up with more pg_temp namespaces, if more distinct backend IDs are used. I think that's fine, and there hasn't been any guarantees on that either. But if those things matter, we could work on the management of free PGPROC entries in proc.c, to assign PGPROC entries and hence backend IDs differently. Reviewed-by: XXX Discussion: https://www.postgresql.org/message-id/8171f1aa-496f-46a6-afc3-c46fe7a9b407@iki.fi --- src/backend/access/transam/twophase.c | 29 +--- src/backend/access/transam/xact.c | 3 +- src/backend/catalog/namespace.c | 2 +- src/backend/postmaster/auxprocess.c | 12 +- src/backend/storage/ipc/procarray.c | 60 +++++++- src/backend/storage/ipc/procsignal.c | 27 ++-- src/backend/storage/ipc/sinvaladt.c | 145 ++++---------------- src/backend/storage/lmgr/lock.c | 16 +-- src/backend/storage/lmgr/proc.c | 14 +- src/backend/utils/activity/backend_status.c | 52 +++---- src/backend/utils/adt/mcxtfuncs.c | 2 +- src/backend/utils/error/csvlog.c | 4 +- src/backend/utils/error/elog.c | 6 +- src/backend/utils/error/jsonlog.c | 4 +- src/backend/utils/init/postinit.c | 10 +- src/backend/utils/time/snapmgr.c | 4 +- src/include/miscadmin.h | 2 - src/include/storage/backendid.h | 12 +- src/include/storage/lock.h | 2 +- src/include/storage/proc.h | 10 +- src/include/storage/procarray.h | 4 + src/include/storage/procsignal.h | 2 +- src/include/storage/sinvaladt.h | 4 - 23 files changed, 170 insertions(+), 256 deletions(-) diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 234c8d08ebc..0b6dd3b3cf0 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -151,7 +151,6 @@ typedef struct GlobalTransactionData { GlobalTransaction next; /* list link for free list */ int pgprocno; /* ID of associated dummy PGPROC */ - BackendId dummyBackendId; /* similar to backend id for backends */ TimestampTz prepared_at; /* time of preparation */ /* @@ -285,20 +284,6 @@ TwoPhaseShmemInit(void) /* associate it with a PGPROC assigned by InitProcGlobal */ gxacts[i].pgprocno = GetNumberFromPGProc(&PreparedXactProcs[i]); - - /* - * Assign a unique ID for each dummy proc, so that the range of - * dummy backend IDs immediately follows the range of normal - * backend IDs. We don't dare to assign a real backend ID to dummy - * procs, because prepared transactions don't take part in cache - * invalidation like a real backend ID would imply, but having a - * unique ID for them is nevertheless handy. This arrangement - * allows you to allocate an array of size (MaxBackends + - * max_prepared_xacts + 1), and have a slot for every backend and - * prepared transaction. Currently multixact.c uses that - * technique. - */ - gxacts[i].dummyBackendId = MaxBackends + 1 + i; } } else @@ -457,7 +442,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid, Assert(LWLockHeldByMeInMode(TwoPhaseStateLock, LW_EXCLUSIVE)); Assert(gxact != NULL); - proc = &ProcGlobal->allProcs[gxact->pgprocno]; + proc = GetPGProcByNumber(gxact->pgprocno); /* Initialize the PGPROC entry */ MemSet(proc, 0, sizeof(PGPROC)); @@ -467,14 +452,12 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid, { /* clone VXID, for TwoPhaseGetXidByVirtualXID() to find */ proc->lxid = MyProc->lxid; - proc->backendId = MyBackendId; } else { Assert(AmStartupProcess() || !IsPostmasterEnvironment); /* GetLockConflicts() uses this to specify a wait on the XID */ proc->lxid = xid; - proc->backendId = InvalidBackendId; } proc->xid = xid; Assert(proc->xmin == InvalidTransactionId); @@ -522,7 +505,7 @@ static void GXactLoadSubxactData(GlobalTransaction gxact, int nsubxacts, TransactionId *children) { - PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno]; + PGPROC *proc = GetPGProcByNumber(gxact->pgprocno); /* We need no extra lock since the GXACT isn't valid yet */ if (nsubxacts > PGPROC_MAX_CACHED_SUBXIDS) @@ -559,7 +542,7 @@ MarkAsPrepared(GlobalTransaction gxact, bool lock_held) * Put it into the global ProcArray so TransactionIdIsInProgress considers * the XID as still running. */ - ProcArrayAdd(&ProcGlobal->allProcs[gxact->pgprocno]); + ProcArrayAdd(GetPGProcByNumber(gxact->pgprocno)); } /* @@ -583,7 +566,7 @@ LockGXact(const char *gid, Oid user) for (i = 0; i < TwoPhaseState->numPrepXacts; i++) { GlobalTransaction gxact = TwoPhaseState->prepXacts[i]; - PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno]; + PGPROC *proc = GetPGProcByNumber(gxact->pgprocno); /* Ignore not-yet-valid GIDs */ if (!gxact->valid) @@ -884,7 +867,7 @@ TwoPhaseGetXidByVirtualXID(VirtualTransactionId vxid, if (!gxact->valid) continue; - proc = &ProcGlobal->allProcs[gxact->pgprocno]; + proc = GetPGProcByNumber(gxact->pgprocno); GET_VXID_FROM_PGPROC(proc_vxid, *proc); if (VirtualTransactionIdEquals(vxid, proc_vxid)) { @@ -919,7 +902,7 @@ TwoPhaseGetDummyBackendId(TransactionId xid, bool lock_held) { GlobalTransaction gxact = TwoPhaseGetGXact(xid, lock_held); - return gxact->dummyBackendId; + return gxact->pgprocno + 1; } /* diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 464858117e0..6c5c6147f13 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2097,9 +2097,8 @@ StartTransaction(void) /* * Advertise it in the proc array. We assume assignment of - * localTransactionId is atomic, and the backendId should be set already. + * localTransactionId is atomic. */ - Assert(MyProc->backendId == vxid.backendId); MyProc->lxid = vxid.localTransactionId; TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId); diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index b610aa62423..e6eea92abd6 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -49,7 +49,7 @@ #include "parser/parse_func.h" #include "storage/ipc.h" #include "storage/lmgr.h" -#include "storage/sinvaladt.h" +#include "storage/procarray.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/catcache.h" diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c index ab86e802f21..39171fea06b 100644 --- a/src/backend/postmaster/auxprocess.c +++ b/src/backend/postmaster/auxprocess.c @@ -107,17 +107,7 @@ AuxiliaryProcessMain(AuxProcType auxtype) BaseInit(); - /* - * Assign the ProcSignalSlot for an auxiliary process. Since it doesn't - * have a BackendId, the slot is statically allocated based on the - * auxiliary process type (MyAuxProcType). Backends use slots indexed in - * the range from 1 to MaxBackends (inclusive), so we use MaxBackends + - * AuxProcType + 1 as the index of the slot for an auxiliary process. - * - * This will need rethinking if we ever want more than one of a particular - * auxiliary process type. - */ - ProcSignalInit(MaxBackends + MyAuxProcType + 1); + ProcSignalInit(); /* * Auxiliary processes don't run transactions, but they may need a diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 5a33eb7f7f7..cd127a5357b 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -2546,7 +2546,7 @@ ProcArrayInstallImportedXmin(TransactionId xmin, continue; /* We are only interested in the specific virtual transaction. */ - if (proc->backendId != sourcevxid->backendId) + if (GetBackendIdFromPGProc(proc) != sourcevxid->backendId) continue; if (proc->lxid != sourcevxid->localTransactionId) continue; @@ -3097,6 +3097,64 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type) return result; } +/* + * BackendIdGetProc -- get a backend's PGPROC given its backend ID + * + * The result may be out of date arbitrarily quickly, so the caller + * must be careful about how this information is used. NULL is + * returned if the backend is not active. + */ +PGPROC * +BackendIdGetProc(int backendID) +{ + PGPROC *result; + + if (backendID < 1 || backendID > ProcGlobal->allProcCount) + return NULL; + result = GetPGProcByBackendId(backendID); + + if (result->pid == 0) + return NULL; + + return result; +} + +/* + * BackendIdGetTransactionIds -- get a backend's transaction status + * + * Get the xid, xmin, nsubxid and overflow status of the backend. The + * result may be out of date arbitrarily quickly, so the caller must be + * careful about how this information is used. + */ +void +BackendIdGetTransactionIds(int backendID, TransactionId *xid, + TransactionId *xmin, int *nsubxid, bool *overflowed) +{ + PGPROC *proc; + + *xid = InvalidTransactionId; + *xmin = InvalidTransactionId; + *nsubxid = 0; + *overflowed = false; + + if (backendID < 1 || backendID > ProcGlobal->allProcCount) + return; + proc = GetPGProcByBackendId(backendID); + + /* Need to lock out additions/removals of backends */ + LWLockAcquire(ProcArrayLock, LW_SHARED); + + if (proc->pid != 0) + { + *xid = proc->xid; + *xmin = proc->xmin; + *nsubxid = proc->subxidStatus.count; + *overflowed = proc->subxidStatus.overflowed; + } + + LWLockRelease(ProcArrayLock); +} + /* * BackendPidGetProc -- get a backend's PGPROC given its PID * diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index e84619e5a58..d1d5bf0c152 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -87,7 +87,7 @@ typedef struct * possible auxiliary process type. (This scheme assumes there is not * more than one of any auxiliary process type at a time.) */ -#define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES) +#define NumProcSignalSlots (MaxBackends + NUM_AUXILIARY_PROCS) /* Check whether the relevant type bit is set in the flags. */ #define BARRIER_SHOULD_CHECK(flags, type) \ @@ -154,24 +154,23 @@ ProcSignalShmemInit(void) /* * ProcSignalInit * Register the current process in the ProcSignal array - * - * The passed index should be my BackendId if the process has one, - * or MaxBackends + aux process type if not. */ void -ProcSignalInit(int pss_idx) +ProcSignalInit(void) { ProcSignalSlot *slot; uint64 barrier_generation; - Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots); - - slot = &ProcSignal->psh_slot[pss_idx - 1]; + if (MyBackendId <= 0) + elog(ERROR, "MyBackendId not set"); + if (MyBackendId > NumProcSignalSlots) + elog(ERROR, "unexpected MyBackendId %d in ProcSignalInit (max %d)", MyBackendId, NumProcSignalSlots); + slot = &ProcSignal->psh_slot[MyBackendId - 1]; /* sanity check */ if (slot->pss_pid != 0) elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty", - MyProcPid, pss_idx); + MyProcPid, (int) (slot - ProcSignal->psh_slot)); /* Clear out any leftover signal reasons */ MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); @@ -200,7 +199,7 @@ ProcSignalInit(int pss_idx) MyProcSignalSlot = slot; /* Set up to release the slot on process exit */ - on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx)); + on_shmem_exit(CleanupProcSignalState, (Datum) 0); } /* @@ -212,11 +211,7 @@ ProcSignalInit(int pss_idx) static void CleanupProcSignalState(int status, Datum arg) { - int pss_idx = DatumGetInt32(arg); - ProcSignalSlot *slot; - - slot = &ProcSignal->psh_slot[pss_idx - 1]; - Assert(slot == MyProcSignalSlot); + ProcSignalSlot *slot = MyProcSignalSlot; /* * Clear MyProcSignalSlot, so that a SIGUSR1 received after this point @@ -233,7 +228,7 @@ CleanupProcSignalState(int status, Datum arg) * infinite loop trying to exit */ elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d", - MyProcPid, pss_idx, (int) slot->pss_pid); + MyProcPid, (int) (slot - ProcSignal->psh_slot), (int) slot->pss_pid); return; /* XXX better to zero the slot anyway? */ } diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index 748a792a854..8105717c578 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -139,7 +139,6 @@ typedef struct ProcState { /* procPid is zero in an inactive ProcState array entry. */ pid_t procPid; /* PID of backend, for signaling */ - PGPROC *proc; /* PGPROC of backend */ /* nextMsgNum is meaningless if procPid == 0 or resetState is true. */ int nextMsgNum; /* next message number to read */ bool resetState; /* backend needs to reset its state */ @@ -173,7 +172,6 @@ typedef struct SISeg int maxMsgNum; /* next message number to be assigned */ int nextThreshold; /* # of messages to call SICleanupQueue */ int lastBackend; /* index of last active procState entry, +1 */ - int maxBackends; /* size of procState array */ slock_t msgnumLock; /* spinlock protecting maxMsgNum */ @@ -183,11 +181,18 @@ typedef struct SISeg SharedInvalidationMessage buffer[MAXNUMMESSAGES]; /* - * Per-backend invalidation state info (has MaxBackends entries). + * Per-backend invalidation state info (has NumProcStateSlots entries). */ ProcState procState[FLEXIBLE_ARRAY_MEMBER]; } SISeg; +/* + * We reserve a slot for each possible BackendId, plus one for each + * possible auxiliary process type. (This scheme assumes there is not + * more than one of any auxiliary process type at a time.) + */ +#define NumProcStateSlots (MaxBackends + NUM_AUXILIARY_PROCS) + static SISeg *shmInvalBuffer; /* pointer to the shared inval buffer */ @@ -205,16 +210,7 @@ SInvalShmemSize(void) Size size; size = offsetof(SISeg, procState); - - /* - * In Hot Standby mode, the startup process requests a procState array - * slot using InitRecoveryTransactionEnvironment(). Even though - * MaxBackends doesn't account for the startup process, it is guaranteed - * to get a free slot. This is because the autovacuum launcher and worker - * processes, which are included in MaxBackends, are not started in Hot - * Standby mode. - */ - size = add_size(size, mul_size(sizeof(ProcState), MaxBackends)); + size = add_size(size, mul_size(sizeof(ProcState), NumProcStateSlots)); return size; } @@ -240,16 +236,14 @@ CreateSharedInvalidationState(void) shmInvalBuffer->maxMsgNum = 0; shmInvalBuffer->nextThreshold = CLEANUP_MIN; shmInvalBuffer->lastBackend = 0; - shmInvalBuffer->maxBackends = MaxBackends; SpinLockInit(&shmInvalBuffer->msgnumLock); /* The buffer[] array is initially all unused, so we need not fill it */ /* Mark all backends inactive, and initialize nextLXID */ - for (i = 0; i < shmInvalBuffer->maxBackends; i++) + for (i = 0; i < NumProcStateSlots; i++) { shmInvalBuffer->procState[i].procPid = 0; /* inactive */ - shmInvalBuffer->procState[i].proc = NULL; shmInvalBuffer->procState[i].nextMsgNum = 0; /* meaningless */ shmInvalBuffer->procState[i].resetState = false; shmInvalBuffer->procState[i].signaled = false; @@ -265,10 +259,17 @@ CreateSharedInvalidationState(void) void SharedInvalBackendInit(bool sendOnly) { - int index; - ProcState *stateP = NULL; + ProcState *stateP; + pid_t oldPid; SISeg *segP = shmInvalBuffer; + if (MyBackendId <= 0) + elog(ERROR, "MyBackendId not set"); + if (MyBackendId > NumProcStateSlots) + elog(PANIC, "unexpected MyBackendId %d in SharedInvalBackendInit (max %d)", + MyBackendId, NumProcStateSlots); + stateP = &segP->procState[MyBackendId - 1]; + /* * This can run in parallel with read operations, but not with write * operations, since SIInsertDataEntries relies on lastBackend to set @@ -276,48 +277,22 @@ SharedInvalBackendInit(bool sendOnly) */ LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); - /* Look for a free entry in the procState array */ - for (index = 0; index < segP->lastBackend; index++) - { - if (segP->procState[index].procPid == 0) /* inactive slot? */ - { - stateP = &segP->procState[index]; - break; - } - } - - if (stateP == NULL) + oldPid = stateP->procPid; + if (oldPid != 0) { - if (segP->lastBackend < segP->maxBackends) - { - stateP = &segP->procState[segP->lastBackend]; - Assert(stateP->procPid == 0); - segP->lastBackend++; - } - else - { - /* - * out of procState slots: MaxBackends exceeded -- report normally - */ - MyBackendId = InvalidBackendId; - LWLockRelease(SInvalWriteLock); - ereport(FATAL, - (errcode(ERRCODE_TOO_MANY_CONNECTIONS), - errmsg("sorry, too many clients already"))); - } + LWLockRelease(SInvalWriteLock); + elog(ERROR, "sinval slot for backend %d is already in use by process %d", + MyBackendId, oldPid); } - MyBackendId = (stateP - &segP->procState[0]) + 1; - - /* Advertise assigned backend ID in MyProc */ - MyProc->backendId = MyBackendId; + if (MyBackendId > segP->lastBackend) + segP->lastBackend = MyBackendId; /* Fetch next local transaction ID into local memory */ nextLocalTransactionId = stateP->nextLXID; /* mark myself active, with all extant messages already read */ stateP->procPid = MyProcPid; - stateP->proc = MyProc; stateP->nextMsgNum = segP->maxMsgNum; stateP->resetState = false; stateP->signaled = false; @@ -328,8 +303,6 @@ SharedInvalBackendInit(bool sendOnly) /* register exit routine to mark my entry inactive at exit */ on_shmem_exit(CleanupInvalidationState, PointerGetDatum(segP)); - - elog(DEBUG4, "my backend ID is %d", MyBackendId); } /* @@ -358,7 +331,6 @@ CleanupInvalidationState(int status, Datum arg) /* Mark myself inactive */ stateP->procPid = 0; - stateP->proc = NULL; stateP->nextMsgNum = 0; stateP->resetState = false; stateP->signaled = false; @@ -374,71 +346,6 @@ CleanupInvalidationState(int status, Datum arg) LWLockRelease(SInvalWriteLock); } -/* - * BackendIdGetProc - * Get the PGPROC structure for a backend, given the backend ID. - * The result may be out of date arbitrarily quickly, so the caller - * must be careful about how this information is used. NULL is - * returned if the backend is not active. - */ -PGPROC * -BackendIdGetProc(int backendID) -{ - PGPROC *result = NULL; - SISeg *segP = shmInvalBuffer; - - /* Need to lock out additions/removals of backends */ - LWLockAcquire(SInvalWriteLock, LW_SHARED); - - if (backendID > 0 && backendID <= segP->lastBackend) - { - ProcState *stateP = &segP->procState[backendID - 1]; - - result = stateP->proc; - } - - LWLockRelease(SInvalWriteLock); - - return result; -} - -/* - * BackendIdGetTransactionIds - * Get the xid, xmin, nsubxid and overflow status of the backend. The - * result may be out of date arbitrarily quickly, so the caller must be - * careful about how this information is used. - */ -void -BackendIdGetTransactionIds(int backendID, TransactionId *xid, - TransactionId *xmin, int *nsubxid, bool *overflowed) -{ - SISeg *segP = shmInvalBuffer; - - *xid = InvalidTransactionId; - *xmin = InvalidTransactionId; - *nsubxid = 0; - *overflowed = false; - - /* Need to lock out additions/removals of backends */ - LWLockAcquire(SInvalWriteLock, LW_SHARED); - - if (backendID > 0 && backendID <= segP->lastBackend) - { - ProcState *stateP = &segP->procState[backendID - 1]; - PGPROC *proc = stateP->proc; - - if (proc != NULL) - { - *xid = proc->xid; - *xmin = proc->xmin; - *nsubxid = proc->subxidStatus.count; - *overflowed = proc->subxidStatus.overflowed; - } - } - - LWLockRelease(SInvalWriteLock); -} - /* * SIInsertDataEntries * Add new invalidation message(s) to the buffer. diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index c70a1adb9ad..0252cacf0a7 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -3625,7 +3625,7 @@ GetLockStatusData(void) proc->fpRelId[f]); instance->holdMask = lockbits << FAST_PATH_LOCKNUMBER_OFFSET; instance->waitLockMode = NoLock; - instance->backend = proc->backendId; + instance->backend = GetBackendIdFromPGProc(proc); instance->lxid = proc->lxid; instance->pid = proc->pid; instance->leaderPid = proc->pid; @@ -3652,14 +3652,14 @@ GetLockStatusData(void) repalloc(data->locks, sizeof(LockInstanceData) * els); } - vxid.backendId = proc->backendId; + vxid.backendId = GetBackendIdFromPGProc(proc); vxid.localTransactionId = proc->fpLocalTransactionId; instance = &data->locks[el]; SET_LOCKTAG_VIRTUALTRANSACTION(instance->locktag, vxid); instance->holdMask = LOCKBIT_ON(ExclusiveLock); instance->waitLockMode = NoLock; - instance->backend = proc->backendId; + instance->backend = GetBackendIdFromPGProc(proc); instance->lxid = proc->lxid; instance->pid = proc->pid; instance->leaderPid = proc->pid; @@ -3712,7 +3712,7 @@ GetLockStatusData(void) instance->waitLockMode = proc->waitLockMode; else instance->waitLockMode = NoLock; - instance->backend = proc->backendId; + instance->backend = GetBackendIdFromPGProc(proc); instance->lxid = proc->lxid; instance->pid = proc->pid; instance->leaderPid = proclock->groupLeader->pid; @@ -3888,7 +3888,7 @@ GetSingleProcBlockerStatusData(PGPROC *blocked_proc, BlockedProcsData *data) instance->waitLockMode = proc->waitLockMode; else instance->waitLockMode = NoLock; - instance->backend = proc->backendId; + instance->backend = GetBackendIdFromPGProc(proc); instance->lxid = proc->lxid; instance->pid = proc->pid; instance->leaderPid = proclock->groupLeader->pid; @@ -4391,7 +4391,7 @@ VirtualXactLockTableInsert(VirtualTransactionId vxid) LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE); - Assert(MyProc->backendId == vxid.backendId); + Assert(MyBackendId == vxid.backendId); Assert(MyProc->fpLocalTransactionId == InvalidLocalTransactionId); Assert(MyProc->fpVXIDLock == false); @@ -4413,8 +4413,6 @@ VirtualXactLockTableCleanup(void) bool fastpath; LocalTransactionId lxid; - Assert(MyProc->backendId != InvalidBackendId); - /* * Clean up shared memory state. */ @@ -4541,7 +4539,7 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait) */ LWLockAcquire(&proc->fpInfoLock, LW_EXCLUSIVE); - if (proc->backendId != vxid.backendId + if (GetBackendIdFromPGProc(proc) != vxid.backendId || proc->fpLocalTransactionId != vxid.localTransactionId) { /* VXID ended */ diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index d9deef3a9bc..ac5096f45aa 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -239,25 +239,25 @@ InitProcGlobal(void) if (i < MaxConnections) { /* PGPROC for normal backend, add to freeProcs list */ - dlist_push_head(&ProcGlobal->freeProcs, &proc->links); + dlist_push_tail(&ProcGlobal->freeProcs, &proc->links); proc->procgloballist = &ProcGlobal->freeProcs; } else if (i < MaxConnections + autovacuum_max_workers + 1) { /* PGPROC for AV launcher/worker, add to autovacFreeProcs list */ - dlist_push_head(&ProcGlobal->autovacFreeProcs, &proc->links); + dlist_push_tail(&ProcGlobal->autovacFreeProcs, &proc->links); proc->procgloballist = &ProcGlobal->autovacFreeProcs; } else if (i < MaxConnections + autovacuum_max_workers + 1 + max_worker_processes) { /* PGPROC for bgworker, add to bgworkerFreeProcs list */ - dlist_push_head(&ProcGlobal->bgworkerFreeProcs, &proc->links); + dlist_push_tail(&ProcGlobal->bgworkerFreeProcs, &proc->links); proc->procgloballist = &ProcGlobal->bgworkerFreeProcs; } else if (i < MaxBackends) { /* PGPROC for walsender, add to walsenderFreeProcs list */ - dlist_push_head(&ProcGlobal->walsenderFreeProcs, &proc->links); + dlist_push_tail(&ProcGlobal->walsenderFreeProcs, &proc->links); proc->procgloballist = &ProcGlobal->walsenderFreeProcs; } @@ -351,6 +351,7 @@ InitProcess(void) (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already"))); } + MyBackendId = GetBackendIdFromPGProc(MyProc); /* * Cross-check that the PGPROC is of the type we expect; if this were not @@ -380,7 +381,6 @@ InitProcess(void) MyProc->xmin = InvalidTransactionId; MyProc->pid = MyProcPid; /* backendId, databaseId and roleId will be filled in later */ - MyProc->backendId = InvalidBackendId; MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->tempNamespaceId = InvalidOid; @@ -561,6 +561,7 @@ InitAuxiliaryProcess(void) ((volatile PGPROC *) auxproc)->pid = MyProcPid; MyProc = auxproc; + MyBackendId = GetBackendIdFromPGProc(MyProc); SpinLockRelease(ProcStructLock); @@ -575,7 +576,6 @@ InitAuxiliaryProcess(void) MyProc->fpLocalTransactionId = InvalidLocalTransactionId; MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; - MyProc->backendId = InvalidBackendId; MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->tempNamespaceId = InvalidOid; @@ -905,6 +905,7 @@ ProcKill(int code, Datum arg) proc = MyProc; MyProc = NULL; + MyBackendId = InvalidBackendId; DisownLatch(&proc->procLatch); procgloballist = proc->procgloballist; @@ -976,6 +977,7 @@ AuxiliaryProcKill(int code, Datum arg) proc = MyProc; MyProc = NULL; + MyBackendId = InvalidBackendId; DisownLatch(&proc->procLatch); SpinLockAcquire(ProcStructLock); diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c index 1a1050c8da1..3d3f7b06723 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -19,6 +19,7 @@ #include "port/atomics.h" /* for memory barriers */ #include "storage/ipc.h" #include "storage/proc.h" /* for MyProc */ +#include "storage/procarray.h" #include "storage/sinvaladt.h" #include "utils/ascii.h" #include "utils/backend_status.h" @@ -29,13 +30,12 @@ /* ---------- * Total number of backends including auxiliary * - * We reserve a slot for each possible BackendId, plus one for each - * possible auxiliary process type. (This scheme assumes there is not - * more than one of any auxiliary process type at a time.) MaxBackends - * includes autovacuum workers and background workers as well. + * We reserve a slot for each possible PGPROC entry, including aux processes. + * (But not including PGPROC entries reserved for prepared xacts; they are not + * real processes.) * ---------- */ -#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES) +#define NumBackendStatSlots (MaxBackends + NUM_AUXILIARY_PROCS) /* ---------- @@ -238,10 +238,9 @@ CreateSharedBackendStatus(void) /* * Initialize pgstats backend activity state, and set up our on-proc-exit - * hook. Called from InitPostgres and AuxiliaryProcessMain. For auxiliary - * process, MyBackendId is invalid. Otherwise, MyBackendId must be set, but we - * must not have started any transaction yet (since the exit hook must run - * after the last transaction exit). + * hook. Called from InitPostgres and AuxiliaryProcessMain. MyBackendId must + * be set, but we must not have started any transaction yet (since the exit + * hook must run after the last transaction exit). * * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful. */ @@ -249,26 +248,9 @@ void pgstat_beinit(void) { /* Initialize MyBEEntry */ - if (MyBackendId != InvalidBackendId) - { - Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends); - MyBEEntry = &BackendStatusArray[MyBackendId - 1]; - } - else - { - /* Must be an auxiliary process */ - Assert(MyAuxProcType != NotAnAuxProcess); - - /* - * Assign the MyBEEntry for an auxiliary process. Since it doesn't - * have a BackendId, the slot is statically allocated based on the - * auxiliary process type (MyAuxProcType). Backends use slots indexed - * in the range from 0 to MaxBackends (exclusive), so we use - * MaxBackends + AuxProcType as the index of the slot for an auxiliary - * process. - */ - MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType]; - } + Assert(MyBackendId != InvalidBackendId); + Assert(MyBackendId >= 1 && MyBackendId <= NumBackendStatSlots); + MyBEEntry = &BackendStatusArray[MyBackendId - 1]; /* Set up a process-exit hook to clean up */ on_shmem_exit(pgstat_beshutdown_hook, 0); @@ -281,12 +263,12 @@ pgstat_beinit(void) * Initialize this backend's entry in the PgBackendStatus array. * Called from InitPostgres. * - * Apart from auxiliary processes, MyBackendId, MyDatabaseId, - * session userid, and application_name must be set for a - * backend (hence, this cannot be combined with pgstat_beinit). - * Note also that we must be inside a transaction if this isn't an aux - * process, as we may need to do encoding conversion on some strings. - * ---------- + * Apart from auxiliary processes, MyDatabaseId, session userid, and + * application_name must already be set (hence, this cannot be combined + * with pgstat_beinit). Note also that we must be inside a transaction + * if this isn't an aux process, as we may need to do encoding conversion + * on some strings. + *---------- */ void pgstat_bestart(void) diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c index 4708d73f5fa..c8c3b8dbaf1 100644 --- a/src/backend/utils/adt/mcxtfuncs.c +++ b/src/backend/utils/adt/mcxtfuncs.c @@ -159,7 +159,7 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS) * have a valid backend id. */ if (proc != NULL) - backendId = proc->backendId; + backendId = GetBackendIdFromPGProc(proc); else proc = AuxiliaryPidGetProc(pid); diff --git a/src/backend/utils/error/csvlog.c b/src/backend/utils/error/csvlog.c index 1b62b07f231..692ee3c1491 100644 --- a/src/backend/utils/error/csvlog.c +++ b/src/backend/utils/error/csvlog.c @@ -152,8 +152,8 @@ write_csvlog(ErrorData *edata) /* Virtual transaction id */ /* keep VXID format in sync with lockfuncs.c */ - if (MyProc != NULL && MyProc->backendId != InvalidBackendId) - appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid); + if (MyProc != NULL) + appendStringInfo(&buf, "%d/%u", MyBackendId, MyProc->lxid); appendStringInfoChar(&buf, ','); /* Transaction id */ diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 2c7a20e3d31..10249f140fe 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -3074,18 +3074,18 @@ log_status_format(StringInfo buf, const char *format, ErrorData *edata) break; case 'v': /* keep VXID format in sync with lockfuncs.c */ - if (MyProc != NULL && MyProc->backendId != InvalidBackendId) + if (MyProc != NULL) { if (padding != 0) { char strfbuf[128]; snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u", - MyProc->backendId, MyProc->lxid); + MyBackendId, MyProc->lxid); appendStringInfo(buf, "%*s", padding, strfbuf); } else - appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid); + appendStringInfo(buf, "%d/%u", MyBackendId, MyProc->lxid); } else if (padding != 0) appendStringInfoSpaces(buf, diff --git a/src/backend/utils/error/jsonlog.c b/src/backend/utils/error/jsonlog.c index 2903561f1c4..3ebe0b7b13c 100644 --- a/src/backend/utils/error/jsonlog.c +++ b/src/backend/utils/error/jsonlog.c @@ -197,8 +197,8 @@ write_jsonlog(ErrorData *edata) /* Virtual transaction id */ /* keep VXID format in sync with lockfuncs.c */ - if (MyProc != NULL && MyProc->backendId != InvalidBackendId) - appendJSONKeyValueFmt(&buf, "vxid", true, "%d/%u", MyProc->backendId, + if (MyProc != NULL) + appendJSONKeyValueFmt(&buf, "vxid", true, "%d/%u", MyBackendId, MyProc->lxid); /* Transaction id */ diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 1ad33671598..e822cd61d5f 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -740,18 +740,10 @@ InitPostgres(const char *in_dbname, Oid dboid, /* * Initialize my entry in the shared-invalidation manager's array of * per-backend data. - * - * Sets up MyBackendId, a unique backend identifier. */ - MyBackendId = InvalidBackendId; - SharedInvalBackendInit(false); - if (MyBackendId > MaxBackends || MyBackendId <= 0) - elog(FATAL, "bad backend ID: %d", MyBackendId); - - /* Now that we have a BackendId, we can participate in ProcSignal */ - ProcSignalInit(MyBackendId); + ProcSignalInit(); /* * Also set up timeout handlers needed for backend operation. We need diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 675e81d82d7..94ddf68fa52 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -1154,7 +1154,7 @@ ExportSnapshot(Snapshot snapshot) * inside the transaction from 1. */ snprintf(path, sizeof(path), SNAPSHOT_EXPORT_DIR "/%08X-%08X-%d", - MyProc->backendId, MyProc->lxid, list_length(exportedSnapshots) + 1); + MyBackendId, MyProc->lxid, list_length(exportedSnapshots) + 1); /* * Copy the snapshot into TopTransactionContext, add it to the @@ -1181,7 +1181,7 @@ ExportSnapshot(Snapshot snapshot) */ initStringInfo(&buf); - appendStringInfo(&buf, "vxid:%d/%u\n", MyProc->backendId, MyProc->lxid); + appendStringInfo(&buf, "vxid:%d/%u\n", MyBackendId, MyProc->lxid); appendStringInfo(&buf, "pid:%d\n", MyProcPid); appendStringInfo(&buf, "dbid:%u\n", MyDatabaseId); appendStringInfo(&buf, "iso:%d\n", XactIsoLevel); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 0b01c1f0935..cbdc61b8576 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -444,8 +444,6 @@ typedef enum WalWriterProcess, WalReceiverProcess, WalSummarizerProcess, - - NUM_AUXPROCTYPES /* Must be last! */ } AuxProcType; extern PGDLLIMPORT AuxProcType MyAuxProcType; diff --git a/src/include/storage/backendid.h b/src/include/storage/backendid.h index 50ac982da19..01387723f79 100644 --- a/src/include/storage/backendid.h +++ b/src/include/storage/backendid.h @@ -14,11 +14,15 @@ #ifndef BACKENDID_H #define BACKENDID_H -/* ---------------- - * -cim 8/17/90 - * ---------------- +/* + * BackendId uniquely identifies an active backend or auxiliary process. It's + * assigned at backend startup after authentication. Note that a backend ID + * can be reused for a different backend immediately after a backend exits. + * + * Backend IDs are assigned starting from 1. For historical reasons, BackendId + * 0 is unused, but InvalidBackendId is defined as -1. */ -typedef int BackendId; /* unique currently active backend identifier */ +typedef int BackendId; #define InvalidBackendId (-1) diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index ed6071f3286..03a920b2254 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -75,7 +75,7 @@ typedef struct ((vxid).backendId = InvalidBackendId, \ (vxid).localTransactionId = InvalidLocalTransactionId) #define GET_VXID_FROM_PGPROC(vxid, proc) \ - ((vxid).backendId = (proc).backendId, \ + ((vxid).backendId = GetBackendIdFromPGProc(&(proc)), \ (vxid).localTransactionId = (proc).lxid) /* MAX_LOCKMODES cannot be larger than the # of bits in LOCKMASK */ diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 37cf8b4067d..0d424a3334d 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -195,7 +195,6 @@ struct PGPROC * data mirrored from this PGPROC */ /* These fields are zero while a backend is still starting up: */ - BackendId backendId; /* This backend's backend ID (if assigned) */ Oid databaseId; /* OID of database this backend is using */ Oid roleId; /* OID of role using this backend */ @@ -405,9 +404,16 @@ extern PGDLLIMPORT PROC_HDR *ProcGlobal; extern PGDLLIMPORT PGPROC *PreparedXactProcs; -/* Accessor for PGPROC given a pgprocno. */ +/* + * Accessors for getting PGPROC given a pgprocno or BackendId, and vice versa. + * + * For historical reasons, some code uses 0-based "proc numbers", while other + * code uses 1-based backend IDs. + */ #define GetPGProcByNumber(n) (&ProcGlobal->allProcs[(n)]) #define GetNumberFromPGProc(proc) ((proc) - &ProcGlobal->allProcs[0]) +#define GetPGProcByBackendId(n) (&ProcGlobal->allProcs[(n) - 1]) +#define GetBackendIdFromPGProc(proc) (GetNumberFromPGProc(proc) + 1) /* * We set aside some extra PGPROC structures for auxiliary processes, diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index f3eba9b7640..3af7577e8c6 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -64,6 +64,10 @@ extern VirtualTransactionId *GetVirtualXIDsDelayingChkpt(int *nvxids, int type); extern bool HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type); +extern PGPROC *BackendIdGetProc(int backendID); +extern void BackendIdGetTransactionIds(int backendID, TransactionId *xid, + TransactionId *xmin, int *nsubxid, + bool *overflowed); extern PGPROC *BackendPidGetProc(int pid); extern PGPROC *BackendPidGetProcWithLock(int pid); extern int BackendXidGetPid(TransactionId xid); diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index 52dcb4c2adf..febdda3611c 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -62,7 +62,7 @@ typedef enum extern Size ProcSignalShmemSize(void); extern void ProcSignalShmemInit(void); -extern void ProcSignalInit(int pss_idx); +extern void ProcSignalInit(void); extern int SendProcSignal(pid_t pid, ProcSignalReason reason, BackendId backendId); diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h index aa3d203efca..c3c97b3f8b7 100644 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@ -31,10 +31,6 @@ extern Size SInvalShmemSize(void); extern void CreateSharedInvalidationState(void); extern void SharedInvalBackendInit(bool sendOnly); -extern PGPROC *BackendIdGetProc(int backendID); -extern void BackendIdGetTransactionIds(int backendID, TransactionId *xid, - TransactionId *xmin, int *nsubxid, - bool *overflowed); extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n); extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize); -- 2.39.2