Skip to content

Commit ab355e3

Browse files
committed
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
1 parent 30b8d6e commit ab355e3

File tree

28 files changed

+282
-322
lines changed

28 files changed

+282
-322
lines changed

src/backend/access/transam/twophase.c

+11-26
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ typedef struct GlobalTransactionData
151151
{
152152
GlobalTransaction next; /* list link for free list */
153153
int pgprocno; /* ID of associated dummy PGPROC */
154-
BackendId dummyBackendId; /* similar to backend id for backends */
155154
TimestampTz prepared_at; /* time of preparation */
156155

157156
/*
@@ -285,20 +284,6 @@ TwoPhaseShmemInit(void)
285284

286285
/* associate it with a PGPROC assigned by InitProcGlobal */
287286
gxacts[i].pgprocno = GetNumberFromPGProc(&PreparedXactProcs[i]);
288-
289-
/*
290-
* Assign a unique ID for each dummy proc, so that the range of
291-
* dummy backend IDs immediately follows the range of normal
292-
* backend IDs. We don't dare to assign a real backend ID to dummy
293-
* procs, because prepared transactions don't take part in cache
294-
* invalidation like a real backend ID would imply, but having a
295-
* unique ID for them is nevertheless handy. This arrangement
296-
* allows you to allocate an array of size (MaxBackends +
297-
* max_prepared_xacts + 1), and have a slot for every backend and
298-
* prepared transaction. Currently multixact.c uses that
299-
* technique.
300-
*/
301-
gxacts[i].dummyBackendId = MaxBackends + 1 + i;
302287
}
303288
}
304289
else
@@ -457,24 +442,24 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
457442
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock, LW_EXCLUSIVE));
458443

459444
Assert(gxact != NULL);
460-
proc = &ProcGlobal->allProcs[gxact->pgprocno];
445+
proc = GetPGProcByNumber(gxact->pgprocno);
461446

462447
/* Initialize the PGPROC entry */
463448
MemSet(proc, 0, sizeof(PGPROC));
464449
dlist_node_init(&proc->links);
465450
proc->waitStatus = PROC_WAIT_STATUS_OK;
466-
if (LocalTransactionIdIsValid(MyProc->lxid))
451+
if (LocalTransactionIdIsValid(MyProc->vxid.lxid))
467452
{
468453
/* clone VXID, for TwoPhaseGetXidByVirtualXID() to find */
469-
proc->lxid = MyProc->lxid;
470-
proc->backendId = MyBackendId;
454+
proc->vxid.lxid = MyProc->vxid.lxid;
455+
proc->vxid.backendId = MyBackendId;
471456
}
472457
else
473458
{
474459
Assert(AmStartupProcess() || !IsPostmasterEnvironment);
475460
/* GetLockConflicts() uses this to specify a wait on the XID */
476-
proc->lxid = xid;
477-
proc->backendId = InvalidBackendId;
461+
proc->vxid.lxid = xid;
462+
proc->vxid.backendId = InvalidBackendId;
478463
}
479464
proc->xid = xid;
480465
Assert(proc->xmin == InvalidTransactionId);
@@ -522,7 +507,7 @@ static void
522507
GXactLoadSubxactData(GlobalTransaction gxact, int nsubxacts,
523508
TransactionId *children)
524509
{
525-
PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno];
510+
PGPROC *proc = GetPGProcByNumber(gxact->pgprocno);
526511

527512
/* We need no extra lock since the GXACT isn't valid yet */
528513
if (nsubxacts > PGPROC_MAX_CACHED_SUBXIDS)
@@ -559,7 +544,7 @@ MarkAsPrepared(GlobalTransaction gxact, bool lock_held)
559544
* Put it into the global ProcArray so TransactionIdIsInProgress considers
560545
* the XID as still running.
561546
*/
562-
ProcArrayAdd(&ProcGlobal->allProcs[gxact->pgprocno]);
547+
ProcArrayAdd(GetPGProcByNumber(gxact->pgprocno));
563548
}
564549

565550
/*
@@ -583,7 +568,7 @@ LockGXact(const char *gid, Oid user)
583568
for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
584569
{
585570
GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
586-
PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno];
571+
PGPROC *proc = GetPGProcByNumber(gxact->pgprocno);
587572

588573
/* Ignore not-yet-valid GIDs */
589574
if (!gxact->valid)
@@ -884,7 +869,7 @@ TwoPhaseGetXidByVirtualXID(VirtualTransactionId vxid,
884869

885870
if (!gxact->valid)
886871
continue;
887-
proc = &ProcGlobal->allProcs[gxact->pgprocno];
872+
proc = GetPGProcByNumber(gxact->pgprocno);
888873
GET_VXID_FROM_PGPROC(proc_vxid, *proc);
889874
if (VirtualTransactionIdEquals(vxid, proc_vxid))
890875
{
@@ -919,7 +904,7 @@ TwoPhaseGetDummyBackendId(TransactionId xid, bool lock_held)
919904
{
920905
GlobalTransaction gxact = TwoPhaseGetGXact(xid, lock_held);
921906

922-
return gxact->dummyBackendId;
907+
return gxact->pgprocno + 1;
923908
}
924909

925910
/*

src/backend/access/transam/xact.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -600,9 +600,9 @@ GetStableLatestTransactionId(void)
600600
static LocalTransactionId lxid = InvalidLocalTransactionId;
601601
static TransactionId stablexid = InvalidTransactionId;
602602

603-
if (lxid != MyProc->lxid)
603+
if (lxid != MyProc->vxid.lxid)
604604
{
605-
lxid = MyProc->lxid;
605+
lxid = MyProc->vxid.lxid;
606606
stablexid = GetTopTransactionIdIfAny();
607607
if (!TransactionIdIsValid(stablexid))
608608
stablexid = ReadNextTransactionId();
@@ -2099,8 +2099,8 @@ StartTransaction(void)
20992099
* Advertise it in the proc array. We assume assignment of
21002100
* localTransactionId is atomic, and the backendId should be set already.
21012101
*/
2102-
Assert(MyProc->backendId == vxid.backendId);
2103-
MyProc->lxid = vxid.localTransactionId;
2102+
Assert(MyProc->vxid.backendId == vxid.backendId);
2103+
MyProc->vxid.lxid = vxid.localTransactionId;
21042104

21052105
TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
21062106

@@ -2289,7 +2289,7 @@ CommitTransaction(void)
22892289
ParallelWorkerReportLastRecEnd(XactLastRecEnd);
22902290
}
22912291

2292-
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
2292+
TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->vxid.lxid);
22932293

22942294
/*
22952295
* Let others know about no transaction in progress by me. Note that this
@@ -2840,7 +2840,7 @@ AbortTransaction(void)
28402840
XLogSetAsyncXactLSN(XactLastRecEnd);
28412841
}
28422842

2843-
TRACE_POSTGRESQL_TRANSACTION_ABORT(MyProc->lxid);
2843+
TRACE_POSTGRESQL_TRANSACTION_ABORT(MyProc->vxid.lxid);
28442844

28452845
/*
28462846
* Let others know about no transaction in progress by me. Note that this

src/backend/catalog/namespace.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
#include "parser/parse_func.h"
5050
#include "storage/ipc.h"
5151
#include "storage/lmgr.h"
52-
#include "storage/sinvaladt.h"
52+
#include "storage/procarray.h"
5353
#include "utils/acl.h"
5454
#include "utils/builtins.h"
5555
#include "utils/catcache.h"

src/backend/commands/sequence.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ setval3_oid(PG_FUNCTION_ARGS)
10771077
static Relation
10781078
lock_and_open_sequence(SeqTable seq)
10791079
{
1080-
LocalTransactionId thislxid = MyProc->lxid;
1080+
LocalTransactionId thislxid = MyProc->vxid.lxid;
10811081

10821082
/* Get the lock if not already held in this xact */
10831083
if (seq->lxid != thislxid)

src/backend/executor/functions.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ init_sql_fcache(FunctionCallInfo fcinfo, Oid collation, bool lazyEvalOK)
799799
lazyEvalOK);
800800

801801
/* Mark fcache with time of creation to show it's valid */
802-
fcache->lxid = MyProc->lxid;
802+
fcache->lxid = MyProc->vxid.lxid;
803803
fcache->subxid = GetCurrentSubTransactionId();
804804

805805
ReleaseSysCache(procedureTuple);
@@ -1081,7 +1081,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
10811081

10821082
if (fcache != NULL)
10831083
{
1084-
if (fcache->lxid != MyProc->lxid ||
1084+
if (fcache->lxid != MyProc->vxid.lxid ||
10851085
!SubTransactionIsActive(fcache->subxid))
10861086
{
10871087
/* It's stale; unlink and delete */

src/backend/postmaster/auxprocess.c

+1-11
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,7 @@ AuxiliaryProcessMain(AuxProcType auxtype)
107107

108108
BaseInit();
109109

110-
/*
111-
* Assign the ProcSignalSlot for an auxiliary process. Since it doesn't
112-
* have a BackendId, the slot is statically allocated based on the
113-
* auxiliary process type (MyAuxProcType). Backends use slots indexed in
114-
* the range from 1 to MaxBackends (inclusive), so we use MaxBackends +
115-
* AuxProcType + 1 as the index of the slot for an auxiliary process.
116-
*
117-
* This will need rethinking if we ever want more than one of a particular
118-
* auxiliary process type.
119-
*/
120-
ProcSignalInit(MaxBackends + MyAuxProcType + 1);
110+
ProcSignalInit();
121111

122112
/*
123113
* Auxiliary processes don't run transactions, but they may need a

src/backend/storage/ipc/procarray.c

+68-5
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
701701
Assert(proc->subxidStatus.count == 0);
702702
Assert(!proc->subxidStatus.overflowed);
703703

704-
proc->lxid = InvalidLocalTransactionId;
704+
proc->vxid.lxid = InvalidLocalTransactionId;
705705
proc->xmin = InvalidTransactionId;
706706

707707
/* be sure this is cleared in abort */
@@ -743,7 +743,7 @@ ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
743743

744744
ProcGlobal->xids[pgxactoff] = InvalidTransactionId;
745745
proc->xid = InvalidTransactionId;
746-
proc->lxid = InvalidLocalTransactionId;
746+
proc->vxid.lxid = InvalidLocalTransactionId;
747747
proc->xmin = InvalidTransactionId;
748748

749749
/* be sure this is cleared in abort */
@@ -930,7 +930,7 @@ ProcArrayClearTransaction(PGPROC *proc)
930930
ProcGlobal->xids[pgxactoff] = InvalidTransactionId;
931931
proc->xid = InvalidTransactionId;
932932

933-
proc->lxid = InvalidLocalTransactionId;
933+
proc->vxid.lxid = InvalidLocalTransactionId;
934934
proc->xmin = InvalidTransactionId;
935935
proc->recoveryConflictPending = false;
936936

@@ -2536,6 +2536,11 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
25362536
/* Get lock so source xact can't end while we're doing this */
25372537
LWLockAcquire(ProcArrayLock, LW_SHARED);
25382538

2539+
/*
2540+
* Find the PGPROC entry of the source transaction. (This could use
2541+
* GetPGProcByBackendId(), unless it's a prepared xact. But this isn't
2542+
* performance critical.)
2543+
*/
25392544
for (index = 0; index < arrayP->numProcs; index++)
25402545
{
25412546
int pgprocno = arrayP->pgprocnos[index];
@@ -2548,9 +2553,9 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
25482553
continue;
25492554

25502555
/* We are only interested in the specific virtual transaction. */
2551-
if (proc->backendId != sourcevxid->backendId)
2556+
if (proc->vxid.backendId != sourcevxid->backendId)
25522557
continue;
2553-
if (proc->lxid != sourcevxid->localTransactionId)
2558+
if (proc->vxid.lxid != sourcevxid->localTransactionId)
25542559
continue;
25552560

25562561
/*
@@ -3099,6 +3104,64 @@ HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
30993104
return result;
31003105
}
31013106

3107+
/*
3108+
* BackendIdGetProc -- get a backend's PGPROC given its backend ID
3109+
*
3110+
* The result may be out of date arbitrarily quickly, so the caller
3111+
* must be careful about how this information is used. NULL is
3112+
* returned if the backend is not active.
3113+
*/
3114+
PGPROC *
3115+
BackendIdGetProc(int backendID)
3116+
{
3117+
PGPROC *result;
3118+
3119+
if (backendID < 1 || backendID > ProcGlobal->allProcCount)
3120+
return NULL;
3121+
result = GetPGProcByBackendId(backendID);
3122+
3123+
if (result->pid == 0)
3124+
return NULL;
3125+
3126+
return result;
3127+
}
3128+
3129+
/*
3130+
* BackendIdGetTransactionIds -- get a backend's transaction status
3131+
*
3132+
* Get the xid, xmin, nsubxid and overflow status of the backend. The
3133+
* result may be out of date arbitrarily quickly, so the caller must be
3134+
* careful about how this information is used.
3135+
*/
3136+
void
3137+
BackendIdGetTransactionIds(int backendID, TransactionId *xid,
3138+
TransactionId *xmin, int *nsubxid, bool *overflowed)
3139+
{
3140+
PGPROC *proc;
3141+
3142+
*xid = InvalidTransactionId;
3143+
*xmin = InvalidTransactionId;
3144+
*nsubxid = 0;
3145+
*overflowed = false;
3146+
3147+
if (backendID < 1 || backendID > ProcGlobal->allProcCount)
3148+
return;
3149+
proc = GetPGProcByBackendId(backendID);
3150+
3151+
/* Need to lock out additions/removals of backends */
3152+
LWLockAcquire(ProcArrayLock, LW_SHARED);
3153+
3154+
if (proc->pid != 0)
3155+
{
3156+
*xid = proc->xid;
3157+
*xmin = proc->xmin;
3158+
*nsubxid = proc->subxidStatus.count;
3159+
*overflowed = proc->subxidStatus.overflowed;
3160+
}
3161+
3162+
LWLockRelease(ProcArrayLock);
3163+
}
3164+
31023165
/*
31033166
* BackendPidGetProc -- get a backend's PGPROC given its PID
31043167
*

src/backend/storage/ipc/procsignal.c

+11-16
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ typedef struct
8787
* possible auxiliary process type. (This scheme assumes there is not
8888
* more than one of any auxiliary process type at a time.)
8989
*/
90-
#define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES)
90+
#define NumProcSignalSlots (MaxBackends + NUM_AUXILIARY_PROCS)
9191

9292
/* Check whether the relevant type bit is set in the flags. */
9393
#define BARRIER_SHOULD_CHECK(flags, type) \
@@ -154,24 +154,23 @@ ProcSignalShmemInit(void)
154154
/*
155155
* ProcSignalInit
156156
* Register the current process in the ProcSignal array
157-
*
158-
* The passed index should be my BackendId if the process has one,
159-
* or MaxBackends + aux process type if not.
160157
*/
161158
void
162-
ProcSignalInit(int pss_idx)
159+
ProcSignalInit(void)
163160
{
164161
ProcSignalSlot *slot;
165162
uint64 barrier_generation;
166163

167-
Assert(pss_idx >= 1 && pss_idx <= NumProcSignalSlots);
168-
169-
slot = &ProcSignal->psh_slot[pss_idx - 1];
164+
if (MyBackendId <= 0)
165+
elog(ERROR, "MyBackendId not set");
166+
if (MyBackendId > NumProcSignalSlots)
167+
elog(ERROR, "unexpected MyBackendId %d in ProcSignalInit (max %d)", MyBackendId, NumProcSignalSlots);
168+
slot = &ProcSignal->psh_slot[MyBackendId - 1];
170169

171170
/* sanity check */
172171
if (slot->pss_pid != 0)
173172
elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty",
174-
MyProcPid, pss_idx);
173+
MyProcPid, MyBackendId - 1);
175174

176175
/* Clear out any leftover signal reasons */
177176
MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t));
@@ -200,7 +199,7 @@ ProcSignalInit(int pss_idx)
200199
MyProcSignalSlot = slot;
201200

202201
/* Set up to release the slot on process exit */
203-
on_shmem_exit(CleanupProcSignalState, Int32GetDatum(pss_idx));
202+
on_shmem_exit(CleanupProcSignalState, (Datum) 0);
204203
}
205204

206205
/*
@@ -212,11 +211,7 @@ ProcSignalInit(int pss_idx)
212211
static void
213212
CleanupProcSignalState(int status, Datum arg)
214213
{
215-
int pss_idx = DatumGetInt32(arg);
216-
ProcSignalSlot *slot;
217-
218-
slot = &ProcSignal->psh_slot[pss_idx - 1];
219-
Assert(slot == MyProcSignalSlot);
214+
ProcSignalSlot *slot = MyProcSignalSlot;
220215

221216
/*
222217
* Clear MyProcSignalSlot, so that a SIGUSR1 received after this point
@@ -233,7 +228,7 @@ CleanupProcSignalState(int status, Datum arg)
233228
* infinite loop trying to exit
234229
*/
235230
elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d",
236-
MyProcPid, pss_idx, (int) slot->pss_pid);
231+
MyProcPid, (int) (slot - ProcSignal->psh_slot), (int) slot->pss_pid);
237232
return; /* XXX better to zero the slot anyway? */
238233
}
239234

0 commit comments

Comments
 (0)