How does PostgreSQL get the transaction number XID
NextXid) = FirstNormalTransactionId; TransactionIdAdvance (ShmemVariableCache- > nextXid); / * * We must store the new XID into the shared ProcArray before releasing * XidGenLock. This ensures that every active XID older than * latestCompletedXid is present in the ProcArray, which is essential for * correct OldestXmin tracking; see src/backend/access/transam/README. * before releasing XidGenLock, store the new XID in the shared data structure ProcArray. * this ensures that every active XID that is older than latestCompletedXid will appear in ProcArray. * this can reduce OldestXmin tracking. Please check src/backend/access/transam/README. * * Note that readers of PGXACT xid fields should be careful to fetch the * value only once, rather than assume they can read a value multiple * times and get the same answer each time. Note we are assuming that * TransactionId and int fetch/store are atomic. * Note that when reading the PGXACT.xid field, be careful to extract it only once, * instead of assuming that the value can be read multiple times and assume that the value returned each time is the same. * at the same time, we assume that TransactionId and int extract / write are atomic. * * The same comments apply to the subxact xid count and overflow fields. * for subxact xid counters and overflow fields, see the note above. * * Use of a write barrier prevents dangerous code rearrangement in this * function; other backends could otherwise e.g. Be examining my subxids * info concurrently, and we don't want them to see an invalid * intermediate state, such as an incremented nxids before the array entry * is filled. * in this function, entering the fortress to write can prevent dangerous things from happening. * otherwise, other background processes may check the subxids information of this process synchronously, but we don't want them to see invalid intermediate states, * for example, add nxid before the array entries are populated. * * Other processes that read nxids should do so before reading xids * elements with a pg_read_barrier () in between, so that they can be sure * not to read an uninitialized array element; see * src/backend/storage/lmgr/README.barrier. * other processes that read nxids should use the pg_read_barrier () function to read the xids bar. * this ensures that they will not read uninitialized array entries. Check the src/backend/storage/lmgr/README.barrier description. * If there's no room to fit a subtransaction XID into PGPROC, set the * cache-overflowed flag instead. This forces readers to look in * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There is a * race-condition window, in that the new XID will not appear as running * until its parent link has been placed into pg_subtrans. However, that * will happen before anyone could possibly have a reason to inquire about * the status of the XID, so it seems OK. (Snapshots taken during this * window * will* include the parent XID, so they will deliver the correct * answer later on when someone does have a reason to inquire.) * if there is no space to put the subtransaction XID into the PGPROC, set the cache-overflow flag. * this forces readers to view the pg_subtrans to map the subtransaction xid to the top-level xid. * there is a race-condition window where the new XID does not appear to be running until its parent link is placed in the pg_subtrans. However, this happens before there is a reason to query the status of XID, so it seems possible. * (snapshots taken in this window * will * contain the parent XID, so when there is a process query later, they will provide the correct answer.) * / if (! isSubXact) MyPgXact- > xid = xid; / * LWLockRelease acts as barrier * / else {int nxids = MyPgXact- > nxids; if (nxids
< PGPROC_MAX_CACHED_SUBXIDS) { MyProc->Subxids.xids [nxids] = xid; pg_write_barrier (); MyPgXact- > nxids = nxids + 1;} else MyPgXact- > overflowed = true;} / / release lock LWLockRelease (XidGenLock); return xid;} III. Tracking analysis
Execute txid_current to trigger a function call
14:26:26 (xdb@ [local]: 5432) testdb=# begin;BEGIN14:26:50 (xdb@ [local]: 5432) testdb=#* select txid_current_if_assigned (); txid_current_if_assigned-- (1 row) 14:26:55 (xdb@ [local]: 5432) testdb=#* select txid_current ()
Start gdb and set breakpoint
(gdb) b GetNewTransactionIdBreakpoint 6 at 0x545e80: file varsup.c, line 56. (gdb) cContinuing.Breakpoint 6, GetNewTransactionId (isSubXact=false) at varsup.c:5656 if (IsInParallelMode ()) (gdb)
View the call stack
(gdb) bt#0 GetNewTransactionId (isSubXact=false) at varsup.c:56#1 0x0000000000546bd9 in AssignTransactionId (s=0xf9c720) at xact.c:557#2 0x000000000054693d in GetTopTransactionId () at xact.c:392#3 0x00000000009fe1f3 in txid_current (fcinfo=0x254c7e0) at txid.c:443#4 0x00000000006cfebd in ExecInterpExpr (state=0x254c6f8, econtext=0x254c3e8, isnull=0x7ffe3d4a31f7) at execExprInterp.c:654#5 0x00000000006d1ac6 in ExecInterpExprStillValid (state=0x254c6f8, econtext=0x254c3e8, isNull=0x7ffe3d4a31f7) at execExprInterp.c:1786#6 0x00000000007140dd in ExecEvalExprSwitchContext (state=0x254c6f8, econtext=0x254c3e8 IsNull=0x7ffe3d4a31f7) at. / src/include/executor/executor.h:303#7 0x000000000071414b in ExecProject (projInfo=0x254c6f0) at. / src/include/executor/executor.h:337#8 0x0000000000714323 in ExecResult (pstate=0x254c2d0) at nodeResult.c:136#9 0x00000000006e4c30 in ExecProcNodeFirst (node=0x254c2d0) at execProcnode.c:445#10 0x00000000006d9974 in ExecProcNode (node=0x254c2d0) at.. / src/include/executor/executor.h:237#11 0x00000000006dc22d in ExecutePlan (estate=0x254c0b8, planstate=0x254c2d0, use_parallel_mode=false Operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x24ccf10, execute_once=true) at execMain.c:1723#12 0x00000000006d9f5c in standard_ExecutorRun (queryDesc=0x256b8e8, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:364#13 0x00000000006d9d7f in ExecutorRun (queryDesc=0x256b8e8, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:307#14 0x00000000008ccf5a in PortalRunSelect (portal=0x250c860, forward=true, count=0, dest=0x24ccf10) at pquery.c:932#15 0x00000000008ccbf3 in PortalRun (portal=0x250c860, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x24ccf10, altdest=0x24ccf10 CompletionTag=0x7ffe3d4a3570 "") at pquery.c:773#16 0x00000000008c6b1e in exec_simple_query (query_string=0x24a6ec8 "select txid_current () ") at postgres.c:1145#17 0x00000000008cae70 in PostgresMain (argc=1, argv=0x24d2dc8, dbname=0x24d2c30" testdb ", username=0x24a3ba8" xdb ") at postgres.c:4182---Type to continue, or q to quit---#18 0x000000000082642b in BackendRun (port=0x24c8c00) at postmaster.c:4361#19 0x0000000000825b8f in BackendStartup (port=0x24c8c00) at postmaster.c:4033#20 0x0000000000821f1c in ServerLoop () at postmaster.c:1706#21 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x24a1b60) at postmaster.c:1379#22 0x00000000007488ef in main (argc=1, argv=0x24a1b60) at main.c:228 (gdb)
Get the XidGenLock lock
(gdb) n63 if (IsBootstrapProcessingMode ()) (gdb) 71 if (RecoveryInProgress ()) (gdb) 74 LWLockAcquire (XidGenLock, LW_EXCLUSIVE); (gdb)
Get the shared memory variable ShmemVariableCache- > nextXid-> 2409
(gdb) 76 xid = ShmemVariableCache- > nextXid; (gdb) 91 if (TransactionIdFollowsOrEquals (xid, ShmemVariableCache- > xidVacLimit)) (gdb) p * ShmemVariableCache$16 = {nextOid = 42628, oidCount = 8191, nextXid = 2409, oldestXid = 561, xidVacLimit = 200000561, xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2408,oldestClogXid = 561} (gdb)
Extended clog
(gdb) n171 ExtendCLOG (xid); (gdb) 172 ExtendCommitTs (xid); (gdb) 173 ExtendSUBTRANS (xid); (gdb) 181 TransactionIdAdvance (ShmemVariableCache- > nextXid); (gdb)
ShmemVariableCache- > nextXid++-> 2410
(gdb) p ShmemVariableCache- > nextXid$17 = 2410
Get process and transaction information
(gdb) n223 volatile PGXACT * mypgxact = MyPgXact; (gdb) 225 if (! isSubXact) (gdb) 226 mypgxact- > xid = xid; (gdb)
Release lock XidGenLock
(gdb) 241 LWLockRelease (XidGenLock); (gdb) p * ShmemVariableCache$18 = {nextOid = 42628, oidCount = 8191, nextXid = 2410, oldestXid = 561, xidVacLimit = 200000561, xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2408, oldestClogXid = 561}
Return xid (2409) to complete the call.
(gdb) n243 return xid; (gdb) 244} (gdb) AssignTransactionId (s=0xf9c720) at xact.c:558558 if (! isSubXact) (gdb) these are all the contents of the article "how to get the transaction number XID by PostgreSQL". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!