PostgreSQL数据库事务系统——获取事务ID
获取事务ID
AssignTransactionId用于向给定的TransactionState填充新的FullTransactionId。事务底层状态此刻必须为TRANS_INPROGRESS。在PG并行worker中不能分配XID。
Assigns a new permanent FullTransactionId to the given TransactionState. We do not assign XIDs to transactions until/unless this is called. Also, any parent TransactionStates that don’t yet have XIDs are assigned one; this maintains the invariant that a child transaction has an XID following its parent’s.
static void AssignTransactionId(TransactionState s) {bool isSubXact = (s->parent != NULL); // 是否处于子事务中ResourceOwner currentOwner;bool log_unknown_top = false;/* Assert that caller didn't screw up */Assert(!FullTransactionIdIsValid(s->fullTransactionId)); Assert(s->state == TRANS_INPROGRESS);/* Workers synchronize transaction state at the beginning of each parallel operation, so we can't account for new XIDs at this point. */if (IsInParallelMode() || IsParallelWorker())elog(ERROR, "cannot assign XIDs during a parallel operation");
如果在子事务中,必须确保父事务有XID,所以子事务的XID拥有落后于父事务。这边对父事务分配XID的逻辑可以参考张树杰的深度理解事务书籍。
/* Ensure parent(s) have XIDs, so that a child always has an XID later than its parent. Mustn't recurse here, or we might get a stack overflow if we're at the bottom of a huge stack of subtransactions none of which have XIDs yet. */if (isSubXact && !FullTransactionIdIsValid(s->parent->fullTransactionId)) {TransactionState p = s->parent;TransactionState *parents;size_t parentOffset = 0;parents = palloc(sizeof(TransactionState) * s->nestingLevel);while (p != NULL && !FullTransactionIdIsValid(p->fullTransactionId)) {parents[parentOffset++] = p;p = p->parent;}/* This is technically a recursive call, but the recursion will never be more than one layer deep. */while (parentOffset != 0) AssignTransactionId(parents[--parentOffset]);pfree(parents);}/* When wal_level=logical, guarantee that a subtransaction's xid can only* be seen in the WAL stream if its toplevel xid has been logged before.* If necessary we log an xact_assignment record with fewer than* PGPROC_MAX_CACHED_SUBXIDS. Note that it is fine if didLogXid isn't set* for a transaction even though it appears in a WAL record, we just might* superfluously log something. That can happen when an xid is included* somewhere inside a wal record, but not in XLogRecord->xl_xid, like in* xl_standby_locks. */if (isSubXact && XLogLogicalInfoActive() && !TopTransactionStateData.didLogXid)log_unknown_top = true;
产生一个新的FullTransactionId,并将其记录到PG_PROC和pg_subtrans中。
/* Generate a new FullTransactionId and record its xid in PG_PROC and pg_subtrans.* NB: we must make the subtrans entry BEFORE the Xid appears anywhere in* shared storage other than PG_PROC; because if there's no room for it in* PG_PROC, the subtrans entry is needed to ensure that other backends see* the Xid as "running". See GetNewTransactionId. */s->fullTransactionId = GetNewTransactionId(isSubXact);if (!isSubXact) XactTopFullTransactionId = s->fullTransactionId;if (isSubXact)SubTransSetParent(XidFromFullTransactionId(s->fullTransactionId),XidFromFullTransactionId(s->parent->fullTransactionId));/* If it's a top-level transaction, the predicate locking system needs to be told about it too. */if (!isSubXact)RegisterPredicateLockingXid(XidFromFullTransactionId(s->fullTransactionId));
和资源管理器处理相关的逻辑
/* Acquire lock on the transaction XID. (We assume this cannot block.) We have to ensure that the lock is assigned to the transaction's own ResourceOwner. */currentOwner = CurrentResourceOwner;CurrentResourceOwner = s->curTransactionOwner;XactLockTableInsert(XidFromFullTransactionId(s->fullTransactionId));CurrentResourceOwner = currentOwner;
/** Every PGPROC_MAX_CACHED_SUBXIDS assigned transaction ids within each* top-level transaction we issue a WAL record for the assignment. We* include the top-level xid and all the subxids that have not yet been* reported using XLOG_XACT_ASSIGNMENT records.** This is required to limit the amount of shared memory required in a hot* standby server to keep track of in-progress XIDs. See notes for* RecordKnownAssignedTransactionIds().** We don't keep track of the immediate parent of each subxid, only the* top-level transaction that each subxact belongs to. This is correct in* recovery only because aborted subtransactions are separately WAL* logged.* This is correct even for the case where several levels above us didn't* have an xid assigned as we recursed up to them beforehand.*/if (isSubXact && XLogStandbyInfoActive()) {unreportedXids[nUnreportedXids] = XidFromFullTransactionId(s->fullTransactionId);nUnreportedXids++;/* ensure this test matches similar one in RecoverPreparedTransactions() */if (nUnreportedXids >= PGPROC_MAX_CACHED_SUBXIDS || log_unknown_top) {xl_xact_assignment xlrec;/* xtop is always set by now because we recurse up transaction stack to the highest unassigned xid and then come back down */xlrec.xtop = GetTopTransactionId();Assert(TransactionIdIsValid(xlrec.xtop));xlrec.nsubxacts = nUnreportedXids;XLogBeginInsert();XLogRegisterData((char *) &xlrec, MinSizeOfXactAssignment);XLogRegisterData((char *) unreportedXids,nUnreportedXids * sizeof(TransactionId));(void) XLogInsert(RM_XACT_ID, XLOG_XACT_ASSIGNMENT);nUnreportedXids = 0;/* mark top, not current xact as having been logged */TopTransactionStateData.didLogXid = true;}}
}
调用者
GetTopTransactionId函数返回main事务的XID(TopTransactionStateData中的XID)
GetCurrentTransactionId返回当前事务的XID(CurrentTransactionState中的XID)
GetTopFullTransactionId函数返回main事务的XID(TopTransactionStateData中的FullTransactionId)
GetCurrentTransactionId返回当前事务的XID(CurrentTransactionState中的FullTransactionId)
TransactionId GetTopTransactionId(void)
TransactionId GetCurrentTransactionId(void)
FullTransactionId GetTopFullTransactionId(void)
FullTransactionId GetCurrentFullTransactionId(void)