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 become 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. (The next
commmit will get rid of the term "backend ID" altogether and make
everything 0-based.)

There is still a 'backendId' field in PGPROC, now part of 'vxid' which
encapsulates the backend ID and local transaction ID together. It's
needed for prepared xacts. For regular backends, the backendId is
always equal to pgprocno + 1, but for prepared xact PGPROC entries,
it's the ID of the original backend that processed the transaction.

Reviewed-by: Andres Freund, Reid Thompson
Discussion: https://www.postgresql.org/message-id/8171f1aa-496f-46a6-afc3-c46fe7a9b407@iki.fi
This commit is contained in:
Heikki Linnakangas 2024-03-03 19:37:28 +02:00
parent 30b8d6e4ce
commit ab355e3a88
28 changed files with 282 additions and 322 deletions

View File

@ -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,24 +442,24 @@ 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));
dlist_node_init(&proc->links);
proc->waitStatus = PROC_WAIT_STATUS_OK;
if (LocalTransactionIdIsValid(MyProc->lxid))
if (LocalTransactionIdIsValid(MyProc->vxid.lxid))
{
/* clone VXID, for TwoPhaseGetXidByVirtualXID() to find */
proc->lxid = MyProc->lxid;
proc->backendId = MyBackendId;
proc->vxid.lxid = MyProc->vxid.lxid;
proc->vxid.backendId = MyBackendId;
}
else
{
Assert(AmStartupProcess() || !IsPostmasterEnvironment);
/* GetLockConflicts() uses this to specify a wait on the XID */
proc->lxid = xid;
proc->backendId = InvalidBackendId;
proc->vxid.lxid = xid;
proc->vxid.backendId = InvalidBackendId;
}
proc->xid = xid;
Assert(proc->xmin == InvalidTransactionId);
@ -522,7 +507,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 +544,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 +568,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 +869,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 +904,7 @@ TwoPhaseGetDummyBackendId(TransactionId xid, bool lock_held)
{
GlobalTransaction gxact = TwoPhaseGetGXact(xid, lock_held);
return gxact->dummyBackendId;
return gxact->pgprocno + 1;
}
/*

View File

@ -600,9 +600,9 @@ GetStableLatestTransactionId(void)
static LocalTransactionId lxid = InvalidLocalTransactionId;
static TransactionId stablexid = InvalidTransactionId;
if (lxid != MyProc->lxid)
if (lxid != MyProc->vxid.lxid)
{
lxid = MyProc->lxid;
lxid = MyProc->vxid.lxid;
stablexid = GetTopTransactionIdIfAny();
if (!TransactionIdIsValid(stablexid))
stablexid = ReadNextTransactionId();
@ -2099,8 +2099,8 @@ StartTransaction(void)
* Advertise it in the proc array. We assume assignment of
* localTransactionId is atomic, and the backendId should be set already.
*/
Assert(MyProc->backendId == vxid.backendId);
MyProc->lxid = vxid.localTransactionId;
Assert(MyProc->vxid.backendId == vxid.backendId);
MyProc->vxid.lxid = vxid.localTransactionId;
TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
@ -2289,7 +2289,7 @@ CommitTransaction(void)
ParallelWorkerReportLastRecEnd(XactLastRecEnd);
}
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->vxid.lxid);
/*
* Let others know about no transaction in progress by me. Note that this
@ -2840,7 +2840,7 @@ AbortTransaction(void)
XLogSetAsyncXactLSN(XactLastRecEnd);
}
TRACE_POSTGRESQL_TRANSACTION_ABORT(MyProc->lxid);
TRACE_POSTGRESQL_TRANSACTION_ABORT(MyProc->vxid.lxid);
/*
* Let others know about no transaction in progress by me. Note that this

View File

@ -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"

View File

@ -1077,7 +1077,7 @@ setval3_oid(PG_FUNCTION_ARGS)
static Relation
lock_and_open_sequence(SeqTable seq)
{
LocalTransactionId thislxid = MyProc->lxid;
LocalTransactionId thislxid = MyProc->vxid.lxid;
/* Get the lock if not already held in this xact */
if (seq->lxid != thislxid)

View File

@ -799,7 +799,7 @@ init_sql_fcache(FunctionCallInfo fcinfo, Oid collation, bool lazyEvalOK)
lazyEvalOK);
/* Mark fcache with time of creation to show it's valid */
fcache->lxid = MyProc->lxid;
fcache->lxid = MyProc->vxid.lxid;
fcache->subxid = GetCurrentSubTransactionId();
ReleaseSysCache(procedureTuple);
@ -1081,7 +1081,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
if (fcache != NULL)
{
if (fcache->lxid != MyProc->lxid ||
if (fcache->lxid != MyProc->vxid.lxid ||
!SubTransactionIsActive(fcache->subxid))
{
/* It's stale; unlink and delete */

View File

@ -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

View File

@ -701,7 +701,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
Assert(proc->subxidStatus.count == 0);
Assert(!proc->subxidStatus.overflowed);
proc->lxid = InvalidLocalTransactionId;
proc->vxid.lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
/* be sure this is cleared in abort */
@ -743,7 +743,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
ProcGlobal->xids[pgxactoff] = InvalidTransactionId;
proc->xid = InvalidTransactionId;
proc->lxid = InvalidLocalTransactionId;
proc->vxid.lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
/* be sure this is cleared in abort */
@ -930,7 +930,7 @@ ProcArrayClearTransaction(PGPROC *proc)
ProcGlobal->xids[pgxactoff] = InvalidTransactionId;
proc->xid = InvalidTransactionId;
proc->lxid = InvalidLocalTransactionId;
proc->vxid.lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
proc->recoveryConflictPending = false;
@ -2536,6 +2536,11 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
/* Get lock so source xact can't end while we're doing this */
LWLockAcquire(ProcArrayLock, LW_SHARED);
/*
* Find the PGPROC entry of the source transaction. (This could use
* GetPGProcByBackendId(), unless it's a prepared xact. But this isn't
* performance critical.)
*/
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
@ -2548,9 +2553,9 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
continue;
/* We are only interested in the specific virtual transaction. */
if (proc->backendId != sourcevxid->backendId)
if (proc->vxid.backendId != sourcevxid->backendId)
continue;
if (proc->lxid != sourcevxid->localTransactionId)
if (proc->vxid.lxid != sourcevxid->localTransactionId)
continue;
/*
@ -3099,6 +3104,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
*

View File

@ -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, MyBackendId - 1);
/* 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? */
}

View File

@ -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 */
@ -172,8 +171,6 @@ typedef struct SISeg
int minMsgNum; /* oldest message still needed */
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 +180,29 @@ typedef struct SISeg
SharedInvalidationMessage buffer[MAXNUMMESSAGES];
/*
* Per-backend invalidation state info (has MaxBackends entries).
* Per-backend invalidation state info.
*
* 'procState' has NumProcStateSlots entries, and is indexed by pgprocno.
* 'numProcs' is the number of slots currently in use, and 'pgprocnos' is
* a dense array of their indexes, to speed up scanning all in-use slots.
*
* 'pgprocnos' is largely redundant with ProcArrayStruct->pgprocnos, but
* having our separate copy avoids contention on ProcArrayLock, and allows
* us to track only the processes that participate in shared cache
* invalidations.
*/
int numProcs;
int *pgprocnos;
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 +220,8 @@ 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)); /* procState */
size = add_size(size, mul_size(sizeof(int), NumProcStateSlots)); /* pgprocnos */
return size;
}
@ -239,23 +246,22 @@ CreateSharedInvalidationState(void)
shmInvalBuffer->minMsgNum = 0;
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;
shmInvalBuffer->procState[i].hasMessages = false;
shmInvalBuffer->procState[i].nextLXID = InvalidLocalTransactionId;
}
shmInvalBuffer->numProcs = 0;
shmInvalBuffer->pgprocnos = (int *) &shmInvalBuffer->procState[i];
}
/*
@ -265,59 +271,41 @@ CreateSharedInvalidationState(void)
void
SharedInvalBackendInit(bool sendOnly)
{
int index;
ProcState *stateP = NULL;
ProcState *stateP;
pid_t oldPid;
SISeg *segP = shmInvalBuffer;
int pgprocno;
if (MyBackendId <= 0)
elog(ERROR, "MyBackendId not set");
if (MyBackendId > NumProcStateSlots)
elog(PANIC, "unexpected MyBackendId %d in SharedInvalBackendInit (max %d)",
MyBackendId, NumProcStateSlots);
pgprocno = MyBackendId - 1;
stateP = &segP->procState[pgprocno];
/*
* This can run in parallel with read operations, but not with write
* operations, since SIInsertDataEntries relies on lastBackend to set
* hasMessages appropriately.
* operations, since SIInsertDataEntries relies on the pgprocnos array to
* set hasMessages appropriately.
*/
LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
/* Look for a free entry in the procState array */
for (index = 0; index < segP->lastBackend; index++)
oldPid = stateP->procPid;
if (oldPid != 0)
{
if (segP->procState[index].procPid == 0) /* inactive slot? */
{
stateP = &segP->procState[index];
break;
}
LWLockRelease(SInvalWriteLock);
elog(ERROR, "sinval slot for backend %d is already in use by process %d",
MyBackendId, (int) oldPid);
}
if (stateP == NULL)
{
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")));
}
}
MyBackendId = (stateP - &segP->procState[0]) + 1;
/* Advertise assigned backend ID in MyProc */
MyProc->backendId = MyBackendId;
shmInvalBuffer->pgprocnos[shmInvalBuffer->numProcs++] = pgprocno;
/* 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 +316,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);
}
/*
@ -345,96 +331,36 @@ CleanupInvalidationState(int status, Datum arg)
{
SISeg *segP = (SISeg *) DatumGetPointer(arg);
ProcState *stateP;
int pgprocno = MyBackendId - 1;
int i;
Assert(PointerIsValid(segP));
LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
stateP = &segP->procState[MyBackendId - 1];
stateP = &segP->procState[pgprocno];
/* Update next local transaction ID for next holder of this backendID */
stateP->nextLXID = nextLocalTransactionId;
/* Mark myself inactive */
stateP->procPid = 0;
stateP->proc = NULL;
stateP->nextMsgNum = 0;
stateP->resetState = false;
stateP->signaled = false;
/* Recompute index of last active backend */
for (i = segP->lastBackend; i > 0; i--)
for (i = segP->numProcs - 1; i >= 0; i--)
{
if (segP->procState[i - 1].procPid != 0)
break;
}
segP->lastBackend = i;
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)
if (segP->pgprocnos[i] == pgprocno)
{
*xid = proc->xid;
*xmin = proc->xmin;
*nsubxid = proc->subxidStatus.count;
*overflowed = proc->subxidStatus.overflowed;
if (i != segP->numProcs - 1)
segP->pgprocnos[i] = segP->pgprocnos[segP->numProcs - 1];
break;
}
}
if (i < 0)
elog(PANIC, "could not find entry in sinval array");
segP->numProcs--;
LWLockRelease(SInvalWriteLock);
}
@ -507,9 +433,9 @@ SIInsertDataEntries(const SharedInvalidationMessage *data, int n)
* these (unlocked) changes will be committed to memory before we exit
* the function.
*/
for (i = 0; i < segP->lastBackend; i++)
for (i = 0; i < segP->numProcs; i++)
{
ProcState *stateP = &segP->procState[i];
ProcState *stateP = &segP->procState[segP->pgprocnos[i]];
stateP->hasMessages = true;
}
@ -677,13 +603,14 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
minsig = min - SIG_THRESHOLD;
lowbound = min - MAXNUMMESSAGES + minFree;
for (i = 0; i < segP->lastBackend; i++)
for (i = 0; i < segP->numProcs; i++)
{
ProcState *stateP = &segP->procState[i];
ProcState *stateP = &segP->procState[segP->pgprocnos[i]];
int n = stateP->nextMsgNum;
/* Ignore if inactive or already in reset state */
if (stateP->procPid == 0 || stateP->resetState || stateP->sendOnly)
/* Ignore if already in reset state */
Assert(stateP->procPid != 0);
if (stateP->resetState || stateP->sendOnly)
continue;
/*
@ -719,11 +646,8 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
{
segP->minMsgNum -= MSGNUMWRAPAROUND;
segP->maxMsgNum -= MSGNUMWRAPAROUND;
for (i = 0; i < segP->lastBackend; i++)
{
/* we don't bother skipping inactive entries here */
segP->procState[i].nextMsgNum -= MSGNUMWRAPAROUND;
}
for (i = 0; i < segP->numProcs; i++)
segP->procState[segP->pgprocnos[i]].nextMsgNum -= MSGNUMWRAPAROUND;
}
/*

View File

@ -137,6 +137,7 @@ InitRecoveryTransactionEnvironment(void)
* are held by vxids and row level locks are held by xids. All queries
* hold AccessShareLocks so never block while we write or lock new rows.
*/
MyProc->vxid.backendId = MyBackendId;
vxid.backendId = MyBackendId;
vxid.localTransactionId = GetNextLocalTransactionId();
VirtualXactLockTableInsert(vxid);

View File

@ -3625,8 +3625,8 @@ GetLockStatusData(void)
proc->fpRelId[f]);
instance->holdMask = lockbits << FAST_PATH_LOCKNUMBER_OFFSET;
instance->waitLockMode = NoLock;
instance->backend = proc->backendId;
instance->lxid = proc->lxid;
instance->vxid.backendId = proc->vxid.backendId;
instance->vxid.localTransactionId = proc->vxid.lxid;
instance->pid = proc->pid;
instance->leaderPid = proc->pid;
instance->fastpath = true;
@ -3652,15 +3652,15 @@ GetLockStatusData(void)
repalloc(data->locks, sizeof(LockInstanceData) * els);
}
vxid.backendId = proc->backendId;
vxid.backendId = proc->vxid.backendId;
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->lxid = proc->lxid;
instance->vxid.backendId = proc->vxid.backendId;
instance->vxid.localTransactionId = proc->vxid.lxid;
instance->pid = proc->pid;
instance->leaderPid = proc->pid;
instance->fastpath = true;
@ -3712,8 +3712,8 @@ GetLockStatusData(void)
instance->waitLockMode = proc->waitLockMode;
else
instance->waitLockMode = NoLock;
instance->backend = proc->backendId;
instance->lxid = proc->lxid;
instance->vxid.backendId = proc->vxid.backendId;
instance->vxid.localTransactionId = proc->vxid.lxid;
instance->pid = proc->pid;
instance->leaderPid = proclock->groupLeader->pid;
instance->fastpath = false;
@ -3888,8 +3888,8 @@ GetSingleProcBlockerStatusData(PGPROC *blocked_proc, BlockedProcsData *data)
instance->waitLockMode = proc->waitLockMode;
else
instance->waitLockMode = NoLock;
instance->backend = proc->backendId;
instance->lxid = proc->lxid;
instance->vxid.backendId = proc->vxid.backendId;
instance->vxid.localTransactionId = proc->vxid.lxid;
instance->pid = proc->pid;
instance->leaderPid = proclock->groupLeader->pid;
instance->fastpath = false;
@ -4374,8 +4374,8 @@ lock_twophase_postabort(TransactionId xid, uint16 info,
* lockers, as we haven't advertised this vxid via the ProcArray yet.
*
* Since MyProc->fpLocalTransactionId will normally contain the same data
* as MyProc->lxid, you might wonder if we really need both. The
* difference is that MyProc->lxid is set and cleared unlocked, and
* as MyProc->vxid.lxid, you might wonder if we really need both. The
* difference is that MyProc->vxid.lxid is set and cleared unlocked, and
* examined by procarray.c, while fpLocalTransactionId is protected by
* fpInfoLock and is used only by the locking subsystem. Doing it this
* way makes it easier to verify that there are no funny race conditions.
@ -4391,7 +4391,7 @@ VirtualXactLockTableInsert(VirtualTransactionId vxid)
LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);
Assert(MyProc->backendId == vxid.backendId);
Assert(MyProc->vxid.backendId == vxid.backendId);
Assert(MyProc->fpLocalTransactionId == InvalidLocalTransactionId);
Assert(MyProc->fpVXIDLock == false);
@ -4413,7 +4413,7 @@ VirtualXactLockTableCleanup(void)
bool fastpath;
LocalTransactionId lxid;
Assert(MyProc->backendId != InvalidBackendId);
Assert(MyProc->vxid.backendId != InvalidBackendId);
/*
* Clean up shared memory state.
@ -4541,7 +4541,7 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
*/
LWLockAcquire(&proc->fpInfoLock, LW_EXCLUSIVE);
if (proc->backendId != vxid.backendId
if (proc->vxid.backendId != vxid.backendId
|| proc->fpLocalTransactionId != vxid.localTransactionId)
{
/* VXID ended */

View File

@ -242,25 +242,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;
}
@ -355,6 +355,7 @@ InitProcess(void)
errmsg("sorry, too many clients already")));
}
MyProcNumber = GetNumberFromPGProc(MyProc);
MyBackendId = GetBackendIdFromPGProc(MyProc);
/*
* Cross-check that the PGPROC is of the type we expect; if this were not
@ -381,14 +382,14 @@ InitProcess(void)
*/
dlist_node_init(&MyProc->links);
MyProc->waitStatus = PROC_WAIT_STATUS_OK;
MyProc->lxid = InvalidLocalTransactionId;
MyProc->fpVXIDLock = false;
MyProc->fpLocalTransactionId = InvalidLocalTransactionId;
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
MyProc->pid = MyProcPid;
/* backendId, databaseId and roleId will be filled in later */
MyProc->backendId = InvalidBackendId;
MyProc->vxid.backendId = MyBackendId;
MyProc->vxid.lxid = InvalidLocalTransactionId;
/* databaseId and roleId will be filled in later */
MyProc->databaseId = InvalidOid;
MyProc->roleId = InvalidOid;
MyProc->tempNamespaceId = InvalidOid;
@ -568,11 +569,11 @@ InitAuxiliaryProcess(void)
/* use volatile pointer to prevent code rearrangement */
((volatile PGPROC *) auxproc)->pid = MyProcPid;
MyProc = auxproc;
SpinLockRelease(ProcStructLock);
MyProc = auxproc;
MyProcNumber = GetNumberFromPGProc(MyProc);
MyBackendId = GetBackendIdFromPGProc(MyProc);
/*
* Initialize all fields of MyProc, except for those previously
@ -580,12 +581,12 @@ InitAuxiliaryProcess(void)
*/
dlist_node_init(&MyProc->links);
MyProc->waitStatus = PROC_WAIT_STATUS_OK;
MyProc->lxid = InvalidLocalTransactionId;
MyProc->fpVXIDLock = false;
MyProc->fpLocalTransactionId = InvalidLocalTransactionId;
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
MyProc->backendId = InvalidBackendId;
MyProc->vxid.backendId = InvalidBackendId;
MyProc->vxid.lxid = InvalidLocalTransactionId;
MyProc->databaseId = InvalidOid;
MyProc->roleId = InvalidOid;
MyProc->tempNamespaceId = InvalidOid;
@ -916,8 +917,14 @@ ProcKill(int code, Datum arg)
proc = MyProc;
MyProc = NULL;
MyProcNumber = INVALID_PGPROCNO;
MyBackendId = InvalidBackendId;
DisownLatch(&proc->procLatch);
/* Mark the proc no longer in use */
proc->pid = 0;
proc->vxid.backendId = InvalidBackendId;
proc->vxid.lxid = InvalidTransactionId;
procgloballist = proc->procgloballist;
SpinLockAcquire(ProcStructLock);
@ -992,12 +999,15 @@ AuxiliaryProcKill(int code, Datum arg)
proc = MyProc;
MyProc = NULL;
MyProcNumber = INVALID_PGPROCNO;
MyBackendId = InvalidBackendId;
DisownLatch(&proc->procLatch);
SpinLockAcquire(ProcStructLock);
/* Mark auxiliary proc no longer in use */
proc->pid = 0;
proc->vxid.backendId = InvalidBackendId;
proc->vxid.lxid = InvalidTransactionId;
/* Update shared estimate of spins_per_delay */
ProcGlobal->spins_per_delay = update_spins_per_delay(ProcGlobal->spins_per_delay);

View File

@ -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)

View File

@ -353,7 +353,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
break;
}
values[10] = VXIDGetDatum(instance->backend, instance->lxid);
values[10] = VXIDGetDatum(instance->vxid.backendId, instance->vxid.localTransactionId);
if (instance->pid != 0)
values[11] = Int32GetDatum(instance->pid);
else

View File

@ -148,19 +148,11 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
PGPROC *proc;
BackendId backendId = InvalidBackendId;
proc = BackendPidGetProc(pid);
/*
* See if the process with given pid is a backend or an auxiliary process.
*
* If the given process is a backend, use its backend id in
* SendProcSignal() later to speed up the operation. Otherwise, don't do
* that because auxiliary processes (except the startup process) don't
* have a valid backend id.
*/
if (proc != NULL)
backendId = proc->backendId;
else
proc = BackendPidGetProc(pid);
if (proc == NULL)
proc = AuxiliaryPidGetProc(pid);
/*
@ -183,6 +175,8 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(false);
}
if (proc != NULL)
backendId = GetBackendIdFromPGProc(proc);
if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
{
/* Again, just a warning to allow loops */

View File

@ -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 && MyProc->vxid.backendId != InvalidBackendId)
appendStringInfo(&buf, "%d/%u", MyProc->vxid.backendId, MyProc->vxid.lxid);
appendStringInfoChar(&buf, ',');
/* Transaction id */

View File

@ -3076,18 +3076,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 && MyProc->vxid.backendId != InvalidBackendId)
{
if (padding != 0)
{
char strfbuf[128];
snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
MyProc->backendId, MyProc->lxid);
MyProc->vxid.backendId, MyProc->vxid.lxid);
appendStringInfo(buf, "%*s", padding, strfbuf);
}
else
appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
appendStringInfo(buf, "%d/%u", MyProc->vxid.backendId, MyProc->vxid.lxid);
}
else if (padding != 0)
appendStringInfoSpaces(buf,

View File

@ -197,9 +197,9 @@ 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,
MyProc->lxid);
if (MyProc != NULL && MyProc->vxid.backendId != InvalidBackendId)
appendJSONKeyValueFmt(&buf, "vxid", true, "%d/%u",
MyProc->vxid.backendId, MyProc->vxid.lxid);
/* Transaction id */
appendJSONKeyValueFmt(&buf, "txid", false, "%u",

View File

@ -742,18 +742,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

View File

@ -1154,7 +1154,8 @@ 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);
MyProc->vxid.backendId, MyProc->vxid.lxid,
list_length(exportedSnapshots) + 1);
/*
* Copy the snapshot into TopTransactionContext, add it to the
@ -1181,7 +1182,7 @@ ExportSnapshot(Snapshot snapshot)
*/
initStringInfo(&buf);
appendStringInfo(&buf, "vxid:%d/%u\n", MyProc->backendId, MyProc->lxid);
appendStringInfo(&buf, "vxid:%d/%u\n", MyProc->vxid.backendId, MyProc->vxid.lxid);
appendStringInfo(&buf, "pid:%d\n", MyProcPid);
appendStringInfo(&buf, "dbid:%u\n", MyDatabaseId);
appendStringInfo(&buf, "iso:%d\n", XactIsoLevel);

View File

@ -454,8 +454,6 @@ typedef enum
WalWriterProcess,
WalReceiverProcess,
WalSummarizerProcess,
NUM_AUXPROCTYPES /* Must be last! */
} AuxProcType;
extern PGDLLIMPORT AuxProcType MyAuxProcType;

View File

@ -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)

View File

@ -74,9 +74,9 @@ typedef struct
#define SetInvalidVirtualTransactionId(vxid) \
((vxid).backendId = InvalidBackendId, \
(vxid).localTransactionId = InvalidLocalTransactionId)
#define GET_VXID_FROM_PGPROC(vxid, proc) \
((vxid).backendId = (proc).backendId, \
(vxid).localTransactionId = (proc).lxid)
#define GET_VXID_FROM_PGPROC(vxid_dst, proc) \
((vxid_dst).backendId = (proc).vxid.backendId, \
(vxid_dst).localTransactionId = (proc).vxid.lxid)
/* MAX_LOCKMODES cannot be larger than the # of bits in LOCKMASK */
#define MAX_LOCKMODES 10
@ -454,8 +454,7 @@ typedef struct LockInstanceData
LOCKTAG locktag; /* tag for locked object */
LOCKMASK holdMask; /* locks held by this PGPROC */
LOCKMODE waitLockMode; /* lock awaited by this PGPROC, if any */
BackendId backend; /* backend ID of this PGPROC */
LocalTransactionId lxid; /* local transaction ID of this PGPROC */
VirtualTransactionId vxid; /* virtual transaction ID of this PGPROC */
TimestampTz waitStart; /* time at which this PGPROC started waiting
* for lock */
int pid; /* pid of this PGPROC */

View File

@ -186,16 +186,31 @@ struct PGPROC
* vacuum must not remove tuples deleted by
* xid >= xmin ! */
LocalTransactionId lxid; /* local id of top-level transaction currently
* being executed by this proc, if running;
* else InvalidLocalTransactionId */
int pid; /* Backend's process ID; 0 if prepared xact */
int pgxactoff; /* offset into various ProcGlobal->arrays with
* data mirrored from this PGPROC */
/*
* Currently running top-level transaction's virtual xid. Together these
* form a VirtualTransactionId, but we don't use that struct because this
* is not atomically assignable as whole, and we want to enforce code to
* consider both parts separately. See comments at VirtualTransactionId.
*/
struct
{
BackendId backendId; /* For regular backends, equal to
* GetBackendIdFromPGProc(proc). For prepared
* xacts, ID of the original backend that
* processed the transaction. For unused
* PGPROC entries, InvalidBackendID. */
LocalTransactionId lxid; /* local id of top-level transaction
* currently * being executed by this
* proc, if running; else
* InvalidLocaltransactionId */
} vxid;
/* 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 */
@ -406,9 +421,16 @@ extern PGDLLIMPORT PROC_HDR *ProcGlobal;
extern PGDLLIMPORT PGPROC *PreparedXactProcs;
/* Accessor for PGPROC given a pgprocno, and vice versa. */
/*
* 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,

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -2211,7 +2211,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
paramLI = setup_param_list(estate, expr);
before_lxid = MyProc->lxid;
before_lxid = MyProc->vxid.lxid;
/*
* If we have a procedure-lifespan resowner, use that to hold the refcount
@ -2232,7 +2232,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
elog(ERROR, "SPI_execute_plan_extended failed executing query \"%s\": %s",
expr->query, SPI_result_code_string(rc));
after_lxid = MyProc->lxid;
after_lxid = MyProc->vxid.lxid;
if (before_lxid != after_lxid)
{
@ -6037,7 +6037,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
int32 *rettypmod)
{
ExprContext *econtext = estate->eval_econtext;
LocalTransactionId curlxid = MyProc->lxid;
LocalTransactionId curlxid = MyProc->vxid.lxid;
ParamListInfo paramLI;
void *save_setup_arg;
bool need_snapshot;
@ -7943,7 +7943,7 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
* functions do; DO blocks have private simple_eval_estates, and private
* cast hash tables to go with them.)
*/
curlxid = MyProc->lxid;
curlxid = MyProc->vxid.lxid;
if (cast_entry->cast_lxid != curlxid || cast_entry->cast_in_use)
{
oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt);
@ -8070,7 +8070,7 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
/* Remember that we have the refcount */
expr->expr_simple_plansource = plansource;
expr->expr_simple_plan = cplan;
expr->expr_simple_plan_lxid = MyProc->lxid;
expr->expr_simple_plan_lxid = MyProc->vxid.lxid;
/* Share the remaining work with the replan code path */
exec_save_simple_expr(expr, cplan);