📄 xact.c
字号:
* * We do not sleep if enableFsync is not turned on, nor if there are * fewer than CommitSiblings other backends with active transactions. */ if (CommitDelay > 0 && enableFsync && CountActiveBackends() >= CommitSiblings) pg_usleep(CommitDelay); XLogFlush(XactLastRecEnd); /* * Now we may update the CLOG, if we wrote a COMMIT record above */ if (markXidCommitted) { TransactionIdCommit(xid); /* to avoid race conditions, the parent must commit first */ TransactionIdCommitTree(nchildren, children); } } else { /* * Asynchronous commit case. * * Report the latest async commit LSN, so that the WAL writer knows to * flush this commit. */ XLogSetAsyncCommitLSN(XactLastRecEnd); /* * We must not immediately update the CLOG, since we didn't flush the * XLOG. Instead, we store the LSN up to which the XLOG must be * flushed before the CLOG may be updated. */ if (markXidCommitted) { TransactionIdAsyncCommit(xid, XactLastRecEnd); /* to avoid race conditions, the parent must commit first */ TransactionIdAsyncCommitTree(nchildren, children, XactLastRecEnd); } } /* * If we entered a commit critical section, leave it now, and let * checkpoints proceed. */ if (markXidCommitted) { MyProc->inCommit = false; END_CRIT_SECTION(); } /* Compute latestXid while we have the child XIDs handy */ latestXid = TransactionIdLatest(xid, nchildren, children); /* Reset XactLastRecEnd until the next transaction writes something */ XactLastRecEnd.xrecoff = 0;cleanup: /* Clean up local data */ if (rels) pfree(rels); return latestXid;}/* * AtCommit_LocalCache */static voidAtCommit_LocalCache(void){ /* * Make catalog changes visible to me for the next command. */ CommandEndInvalidationMessages();}/* * AtCommit_Memory */static voidAtCommit_Memory(void){ /* * Now that we're "out" of a transaction, have the system allocate things * in the top memory context instead of per-transaction contexts. */ MemoryContextSwitchTo(TopMemoryContext); /* * Release all transaction-local memory. */ Assert(TopTransactionContext != NULL); MemoryContextDelete(TopTransactionContext); TopTransactionContext = NULL; CurTransactionContext = NULL; CurrentTransactionState->curTransactionContext = NULL;}/* ---------------------------------------------------------------- * CommitSubTransaction stuff * ---------------------------------------------------------------- *//* * AtSubCommit_Memory */static voidAtSubCommit_Memory(void){ TransactionState s = CurrentTransactionState; Assert(s->parent != NULL); /* Return to parent transaction level's memory context. */ CurTransactionContext = s->parent->curTransactionContext; MemoryContextSwitchTo(CurTransactionContext); /* * Ordinarily we cannot throw away the child's CurTransactionContext, * since the data it contains will be needed at upper commit. However, if * there isn't actually anything in it, we can throw it away. This avoids * a small memory leak in the common case of "trivial" subxacts. */ if (MemoryContextIsEmpty(s->curTransactionContext)) { MemoryContextDelete(s->curTransactionContext); s->curTransactionContext = NULL; }}/* * AtSubCommit_childXids * * Pass my own XID and my child XIDs up to my parent as committed children. */static voidAtSubCommit_childXids(void){ TransactionState s = CurrentTransactionState; int new_nChildXids; Assert(s->parent != NULL); /* * The parent childXids array will need to hold my XID and all my * childXids, in addition to the XIDs already there. */ new_nChildXids = s->parent->nChildXids + s->nChildXids + 1; /* Allocate or enlarge the parent array if necessary */ if (s->parent->maxChildXids < new_nChildXids) { int new_maxChildXids; TransactionId *new_childXids; /* * Make it 2x what's needed right now, to avoid having to enlarge it * repeatedly. But we can't go above MaxAllocSize. (The latter * limit is what ensures that we don't need to worry about integer * overflow here or in the calculation of new_nChildXids.) */ new_maxChildXids = Min(new_nChildXids * 2, (int) (MaxAllocSize / sizeof(TransactionId))); if (new_maxChildXids < new_nChildXids) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("maximum number of committed subtransactions (%d) exceeded", (int) (MaxAllocSize / sizeof(TransactionId))))); /* * We keep the child-XID arrays in TopTransactionContext; this avoids * setting up child-transaction contexts for what might be just a few * bytes of grandchild XIDs. */ if (s->parent->childXids == NULL) new_childXids = MemoryContextAlloc(TopTransactionContext, new_maxChildXids * sizeof(TransactionId)); else new_childXids = repalloc(s->parent->childXids, new_maxChildXids * sizeof(TransactionId)); s->parent->childXids = new_childXids; s->parent->maxChildXids = new_maxChildXids; } /* * Copy all my XIDs to parent's array. * * Note: We rely on the fact that the XID of a child always follows that * of its parent. By copying the XID of this subtransaction before the * XIDs of its children, we ensure that the array stays ordered. Likewise, * all XIDs already in the array belong to subtransactions started and * subcommitted before us, so their XIDs must precede ours. */ s->parent->childXids[s->parent->nChildXids] = s->transactionId; if (s->nChildXids > 0) memcpy(&s->parent->childXids[s->parent->nChildXids + 1], s->childXids, s->nChildXids * sizeof(TransactionId)); s->parent->nChildXids = new_nChildXids; /* Release child's array to avoid leakage */ if (s->childXids != NULL) pfree(s->childXids); /* We must reset these to avoid double-free if fail later in commit */ s->childXids = NULL; s->nChildXids = 0; s->maxChildXids = 0;}/* * RecordSubTransactionCommit */static voidRecordSubTransactionCommit(void){ TransactionId xid = GetCurrentTransactionIdIfAny(); /* * We do not log the subcommit in XLOG; it doesn't matter until the * top-level transaction commits. * * We must mark the subtransaction subcommitted in the CLOG if it had a * valid XID assigned. If it did not, nobody else will ever know about * the existence of this subxact. We don't have to deal with deletions * scheduled for on-commit here, since they'll be reassigned to our parent * (who might still abort). */ if (TransactionIdIsValid(xid)) { /* XXX does this really need to be a critical section? */ START_CRIT_SECTION(); /* Record subtransaction subcommit */ TransactionIdSubCommit(xid); END_CRIT_SECTION(); }}/* ---------------------------------------------------------------- * AbortTransaction stuff * ---------------------------------------------------------------- *//* * RecordTransactionAbort * * Returns latest XID among xact and its children, or InvalidTransactionId * if the xact has no XID. (We compute that here just because it's easier.) */static TransactionIdRecordTransactionAbort(bool isSubXact){ TransactionId xid = GetCurrentTransactionIdIfAny(); TransactionId latestXid; int nrels; RelFileNode *rels; int nchildren; TransactionId *children; XLogRecData rdata[3]; int lastrdata = 0; xl_xact_abort xlrec; /* * If we haven't been assigned an XID, nobody will care whether we aborted * or not. Hence, we're done in that case. It does not matter if we have * rels to delete (note that this routine is not responsible for actually * deleting 'em). We cannot have any child XIDs, either. */ if (!TransactionIdIsValid(xid)) { /* Reset XactLastRecEnd until the next transaction writes something */ if (!isSubXact) XactLastRecEnd.xrecoff = 0; return InvalidTransactionId; } /* * We have a valid XID, so we should write an ABORT record for it. * * We do not flush XLOG to disk here, since the default assumption after a * crash would be that we aborted, anyway. For the same reason, we don't * need to worry about interlocking against checkpoint start. */ /* * Check that we haven't aborted halfway through RecordTransactionCommit. */ if (TransactionIdDidCommit(xid)) elog(PANIC, "cannot abort transaction %u, it was already committed", xid); /* Fetch the data we need for the abort record */ nrels = smgrGetPendingDeletes(false, &rels, NULL); nchildren = xactGetCommittedChildren(&children); /* XXX do we really need a critical section here? */ START_CRIT_SECTION(); /* Write the ABORT record */ if (isSubXact) xlrec.xact_time = GetCurrentTimestamp(); else { SetCurrentTransactionStopTimestamp(); xlrec.xact_time = xactStopTimestamp; } xlrec.nrels = nrels; xlrec.nsubxacts = nchildren; rdata[0].data = (char *) (&xlrec); rdata[0].len = MinSizeOfXactAbort; rdata[0].buffer = InvalidBuffer; /* dump rels to delete */ if (nrels > 0) { rdata[0].next = &(rdata[1]); rdata[1].data = (char *) rels; rdata[1].len = nrels * sizeof(RelFileNode); rdata[1].buffer = InvalidBuffer; lastrdata = 1; } /* dump committed child Xids */ if (nchildren > 0) { rdata[lastrdata].next = &(rdata[2]); rdata[2].data = (char *) children; rdata[2].len = nchildren * sizeof(TransactionId); rdata[2].buffer = InvalidBuffer; lastrdata = 2; } rdata[lastrdata].next = NULL; (void) XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, rdata); /* * Mark the transaction aborted in clog. This is not absolutely necessary * but we may as well do it while we are here; also, in the subxact case * it is helpful because XactLockTableWait makes use of it to avoid * waiting for already-aborted subtransactions. It is OK to do it without * having flushed the ABORT record to disk, because in event of a crash * we'd be assumed to have aborted anyway. * * The ordering here isn't critical but it seems best to mark the parent * first. This assures an atomic transition of all the subtransactions to * aborted state from the point of view of concurrent * TransactionIdDidAbort calls. */ TransactionIdAbort(xid); TransactionIdAbortTree(nchildren, children); END_CRIT_SECTION(); /* Compute latestXid while we have the child XIDs handy */ latestXid = TransactionIdLatest(xid, nchildren, children); /* * If we're aborting a subtransaction, we can immediately remove failed * XIDs from PGPROC's cache of running child XIDs. We do that here for * subxacts, because we already have the child XID array at hand. For * main xacts, the equivalent happens just after this function returns. */ if (isSubXact) XidCacheRemoveRunningXids(xid, nchildren, children, latestXid); /* Reset XactLastRecEnd until the next transaction writes something */ if (!isSubXact) XactLastRecEnd.xrecoff = 0; /* And clean up local data */ if (rels) pfree(rels); return latestXid;}/* * AtAbort_Memory */static voidAtAbort_Memory(void){ /* * Switch into TransactionAbortContext, which should have some free space * even if nothing else does. We'll work in this context until we've * finished cleaning up. * * It is barely possible to get here when we've not been able to create * TransactionAbortContext yet; if so use TopMemoryContext. */ if (TransactionAbortContext != NULL) MemoryContextSwitchTo(TransactionAbortContext); else MemoryContextSwitchTo(TopMemoryContext);}/* * AtSubAbort_Memory */static voidAtSubAbort_Memory(void){ Assert(TransactionAbortContext != NULL); MemoryContextSwitchTo(TransactionAbortContext);}/* * AtAbort_ResourceOwner */static voidAtAbort_ResourceOwner(void){ /* * Make sure we have a valid ResourceOwner, if possible (else it will be * NULL, which is OK) */ CurrentResourceOwner = TopTransactionResourceOwner;}/* * AtSubAbort_ResourceOwner */static voidAtSubAbort_ResourceOwner(void){ TransactionState s = CurrentTransactionState; /* Make sure we have a valid ResourceOwner */ CurrentResourceOwner = s->curTransactionOwner;}/* * AtSubAbort_childXids */static voidAtSubAbort_childXids(void){ TransactionState s = CurrentTransactionState; /* * We keep the child-XID arrays in TopTransactionContext (see * AtSubCommit_childXids). This means we'd better free the array * explicitly at abort to avoid leakage. */ if (s->childXids != NULL) pfree(s->childXids); s->childXids = NULL; s->nChildXids = 0; s->maxChildXids = 0;}/* ---------------------------------------------------------------- * CleanupTransaction stuff * ---------------------------------------------------------------- *//* * AtCleanup_Memory */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -