PostgreSQL 源碼解讀(121)- MVCC#6(獲取事務號-實現函數)

本節介紹了PostgreSQL獲取事務號XID的邏輯,主要解析了函數AssignTransactionId->GetNewTransactionId的實現邏輯。

在GetNewTransactionId函數中,檢查是否可以安全的分配XID.
這可以防止由於XID wraparound而導致的災難性數據丟失.
基本規則是:
如果超過了xidVacLimit,開始嘗試強制autovacuum循環.
如果超過了xidWarnLimit,開始發出警告.
如果超過了xidStopLimit,拒絕執行事務,直至以單用戶模式運行.
(這爲DBA提供了一個逃生通道,使他們能夠通過數據庫早期的安全性檢測,進入數據庫進行維護)

ShmemVariableCache->xidVacLimit --> 200,000,561 --> 大於等於該值,觸發自動vacuum
ShmemVariableCache->xidWarnLimit --> 2,136,484,208 --> 大於等於該值,系統報警
ShmemVariableCache->xidStopLimit --> 2,146,484,208 --> 大於等於該值,系統不允許執行事務,使用單用戶模式處理

一、數據結構

TransactionState
事務狀態結構體

/*
 *  transaction states - transaction state from server perspective
 *  事務狀態枚舉 - 服務器視角的事務狀態
 */
typedef enum TransState
{
    TRANS_DEFAULT,              /* idle 空閒 */
    TRANS_START,                /* transaction starting 事務啓動 */
    TRANS_INPROGRESS,           /* inside a valid transaction 進行中 */
    TRANS_COMMIT,               /* commit in progress 提交中 */
    TRANS_ABORT,                /* abort in progress 回滾中 */
    TRANS_PREPARE               /* prepare in progress 準備中 */
} TransState;

/*
 *  transaction block states - transaction state of client queries
 *  事務塊狀態 - 客戶端查詢的事務狀態
 *
 * Note: the subtransaction states are used only for non-topmost
 * transactions; the others appear only in the topmost transaction.
 * 注意:subtransaction只用於非頂層事務;其他字段用於頂層事務.
 */
typedef enum TBlockState
{
    /* not-in-transaction-block states 未進入事務塊狀態 */
    TBLOCK_DEFAULT,             /* idle 空閒  */
    TBLOCK_STARTED,             /* running single-query transaction 單個查詢事務 */

    /* transaction block states 事務塊狀態 */
    TBLOCK_BEGIN,               /* starting transaction block 開始事務塊 */
    TBLOCK_INPROGRESS,          /* live transaction 進行中 */
    TBLOCK_IMPLICIT_INPROGRESS, /* live transaction after implicit BEGIN 隱式事務,進行中 */
    TBLOCK_PARALLEL_INPROGRESS, /* live transaction inside parallel worker 並行worker中的事務,進行中 */
    TBLOCK_END,                 /* COMMIT received 接收到COMMIT */
    TBLOCK_ABORT,               /* failed xact, awaiting ROLLBACK 失敗,等待ROLLBACK */
    TBLOCK_ABORT_END,           /* failed xact, ROLLBACK received 失敗,已接收ROLLBACK */
    TBLOCK_ABORT_PENDING,       /* live xact, ROLLBACK received 進行中,接收到ROLLBACK */
    TBLOCK_PREPARE,             /* live xact, PREPARE received 進行中,接收到PREPARE */

    /* subtransaction states 子事務狀態 */
    TBLOCK_SUBBEGIN,            /* starting a subtransaction 開啓 */
    TBLOCK_SUBINPROGRESS,       /* live subtransaction 進行中 */
    TBLOCK_SUBRELEASE,          /* RELEASE received 接收到RELEASE */
    TBLOCK_SUBCOMMIT,           /* COMMIT received while TBLOCK_SUBINPROGRESS 進行中,接收到COMMIT */
    TBLOCK_SUBABORT,            /* failed subxact, awaiting ROLLBACK 失敗,等待ROLLBACK */
    TBLOCK_SUBABORT_END,        /* failed subxact, ROLLBACK received 失敗,已接收ROLLBACK */
    TBLOCK_SUBABORT_PENDING,    /* live subxact, ROLLBACK received 進行中,接收到ROLLBACK */
    TBLOCK_SUBRESTART,          /* live subxact, ROLLBACK TO received 進行中,接收到ROLLBACK TO */
    TBLOCK_SUBABORT_RESTART     /* failed subxact, ROLLBACK TO received 失敗,已接收ROLLBACK TO */
} TBlockState;

/*
 *  transaction state structure
 *  事務狀態結構體
 */
typedef struct TransactionStateData
{
    //事務ID
    TransactionId transactionId;    /* my XID, or Invalid if none */
    //子事務ID
    SubTransactionId subTransactionId;  /* my subxact ID */
    //保存點名稱
    char       *name;           /* savepoint name, if any */
    //保存點級別
    int         savepointLevel; /* savepoint level */
    //低級別的事務狀態
    TransState  state;          /* low-level state */
    //高級別的事務狀態
    TBlockState blockState;     /* high-level state */
    //事務嵌套深度
    int         nestingLevel;   /* transaction nesting depth */
    //GUC上下文嵌套深度
    int         gucNestLevel;   /* GUC context nesting depth */
    //事務生命週期上下文
    MemoryContext curTransactionContext;    /* my xact-lifetime context */
    //查詢資源
    ResourceOwner curTransactionOwner;  /* my query resources */
    //按XID順序保存的已提交的子事務ID
    TransactionId *childXids;   /* subcommitted child XIDs, in XID order */
    //childXids數組大小
    int         nChildXids;     /* # of subcommitted child XIDs */
    //分配的childXids數組空間
    int         maxChildXids;   /* allocated size of childXids[] */
    //上一個CurrentUserId
    Oid         prevUser;       /* previous CurrentUserId setting */
    //上一個SecurityRestrictionContext
    int         prevSecContext; /* previous SecurityRestrictionContext */
    //上一事務是否只讀?
    bool        prevXactReadOnly;   /* entry-time xact r/o state */
    //是否處於Recovery?
    bool        startedInRecovery;  /* did we start in recovery? */
    //XID是否已保存在WAL Record中?
    bool        didLogXid;      /* has xid been included in WAL record? */
    //Enter/ExitParallelMode計數器
    int         parallelModeLevel;  /* Enter/ExitParallelMode counter */
    //父事務狀態
    struct TransactionStateData *parent;    /* back link to parent */
} TransactionStateData;

//結構體指針
typedef TransactionStateData *TransactionState;

ShmemVariableCache
VariableCache是共享內存中的一種數據結構,用於跟蹤OID和XID分配狀態。
ShmemVariableCache-->共享內存中的指針

/*
 * VariableCache is a data structure in shared memory that is used to track
 * OID and XID assignment state.  For largely historical reasons, there is
 * just one struct with different fields that are protected by different
 * LWLocks.
 * VariableCache是共享內存中的一種數據結構,用於跟蹤OID和XID分配狀態。
 * 由於歷史原因,這個結構體有不同的字段,由不同的LWLocks保護。
 *
 * Note: xidWrapLimit and oldestXidDB are not "active" values, but are
 * used just to generate useful messages when xidWarnLimit or xidStopLimit
 * are exceeded.
 * 注意:xidWrapLimit和oldestXidDB是不"活躍"的值,在xidWarnLimit或xidStopLimit
 *   超出限制時用於產生有用的信息.
 */
typedef struct VariableCacheData
{
    /*
     * These fields are protected by OidGenLock.
     * 這些域字段通過OidGenLock字段保護
     */
    //下一個待分配的OID
    Oid         nextOid;        /* next OID to assign */
    //在必須執行XLOG work前可用OIDs
    uint32      oidCount;       /* OIDs available before must do XLOG work */

    /*
     * These fields are protected by XidGenLock.
     * 這些字段通過XidGenLock鎖保護.
     */
    //下一個待分配的事務ID
    TransactionId nextXid;      /* next XID to assign */

    //集羣範圍內最小datfrozenxid
    TransactionId oldestXid;    /* cluster-wide minimum datfrozenxid */
    //在該XID開始強制執行autovacuum
    TransactionId xidVacLimit;  /* start forcing autovacuums here */
    //在該XID開始提出警告
    TransactionId xidWarnLimit; /* start complaining here */
    //在該XID開外,拒絕生成下一個XID
    TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */
    //"世界末日"XID,需回捲
    TransactionId xidWrapLimit; /* where the world ends */
    //持有最小datfrozenxid的DB
    Oid         oldestXidDB;    /* database with minimum datfrozenxid */

    /*
     * These fields are protected by CommitTsLock
     * 這些字段通過CommitTsLock鎖保護
     */
    TransactionId oldestCommitTsXid;
    TransactionId newestCommitTsXid;

    /*
     * These fields are protected by ProcArrayLock.
     * 這些字段通過ProcArrayLock鎖保護
     */
    TransactionId latestCompletedXid;   /* newest XID that has committed or
                                         * aborted */

    /*
     * These fields are protected by CLogTruncationLock
     * 這些字段通過CLogTruncationLock鎖保護
     */
    //clog中最古老的XID
    TransactionId oldestClogXid;    /* oldest it's safe to look up in clog */

} VariableCacheData;
//結構體指針
typedef VariableCacheData *VariableCache;

/* pointer to "variable cache" in shared memory (set up by shmem.c) */
//共享內存中的指針(通過shmem.c設置)
VariableCache ShmemVariableCache = NULL;

二、源碼解讀

AssignTransactionId函數,給定的TransactionState分配一個新的持久化事務號XID,在此函數調用前,不會爲事務分配XIDs.GetNewTransactionId是獲取事務ID實際執行的地方,該函數從共享內存變量ShmemVariableCache中獲取nextXid,+1後作爲新的XID.

/*
 * Allocate the next XID for a new transaction or subtransaction.
 * 爲新事務或新子事務分配XID
 *
 * The new XID is also stored into MyPgXact before returning.
 * 在返回前,XID會存儲在全局變量MyPgXact中
 *
 * Note: when this is called, we are actually already inside a valid
 * transaction, since XIDs are now not allocated until the transaction
 * does something.  So it is safe to do a database lookup if we want to
 * issue a warning about XID wrap.
 * 注意:在該函數調用時,我們實際上已在一個有效的事務中,因爲XIDs在事務不做些事情前不會分配.
 * 因此,如果我們想發出關於XID wrap回捲的警告,那麼進行數據庫查找是安全的。
 */
TransactionId
GetNewTransactionId(bool isSubXact)
{
    TransactionId xid;

    /*
     * Workers synchronize transaction state at the beginning of each parallel
     * operation, so we can't account for new XIDs after that point.
     * 在每個並行操作前,Parallel Workers同步事務狀態,
     *   因此我們不能在這時候請求XIDs
     */
    if (IsInParallelMode())
        elog(ERROR, "cannot assign TransactionIds during a parallel operation");

    /*
     * During bootstrap initialization, we return the special bootstrap
     * transaction id.
     * 在宇宙初啓時,返回特別的bootstrap事務ID
     */
    if (IsBootstrapProcessingMode())
    {
        Assert(!isSubXact);
        MyPgXact->xid = BootstrapTransactionId;
        return BootstrapTransactionId;//--> 1
    }

    /* safety check, we should never get this far in a HS standby */
    * 安全檢查
    if (RecoveryInProgress())
        elog(ERROR, "cannot assign TransactionIds during recovery");

    LWLockAcquire(XidGenLock, LW_EXCLUSIVE);

    //從共享內存中獲取下一個XID
    xid = ShmemVariableCache->nextXid;

    /*----------
     * Check to see if it's safe to assign another XID.  This protects against
     * catastrophic data loss due to XID wraparound.  The basic rules are:
     * 檢查是否可以安全的分配另外一個XID.
     * 這可以防止由於XID wraparound而導致的災難性數據丟失.
     * 基本規則是:
     *
     * If we're past xidVacLimit, start trying to force autovacuum cycles.
     * If we're past xidWarnLimit, start issuing warnings.
     * If we're past xidStopLimit, refuse to execute transactions, unless
     * we are running in single-user mode (which gives an escape hatch
     * to the DBA who somehow got past the earlier defenses).
     * 如果超過了xidVacLimit,開始嘗試強制autovacuum循環.
     * 如果超過了xidWarnLimit,開始發出警告.
     * 如果超過了xidStopLimit,拒絕執行事務,直至以單用戶模式運行.
     * (這爲DBA提供了一個逃生通道,使他們能夠通過數據庫早期的安全性檢測)
     *
     * Note that this coding also appears in GetNewMultiXactId.
     * 注意這部分代碼在GetNewMultiXactId中也會出現.
     *----------
     */
    //TransactionIdFollowsOrEquals --> is id1 logically >= id2?
    if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit))
    {
        //xid >= ShmemVariableCache->xidVacLimit
        /*
         * For safety's sake, we release XidGenLock while sending signals,
         * warnings, etc.  This is not so much because we care about
         * preserving concurrency in this situation, as to avoid any
         * possibility of deadlock while doing get_database_name(). First,
         * copy all the shared values we'll need in this path.
         * 爲了安全起見,我們在發送信號、警告等時釋放XidGenLock。
         * 這並不是因爲我們關心在這種情況下併發性,
         *   而是因爲在執行get_database_name()時要避免出現死鎖
         */
        //獲取相關XID
        TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit;
        TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit;
        TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit;
        Oid         oldest_datoid = ShmemVariableCache->oldestXidDB;

        LWLockRelease(XidGenLock);

        /*
         * To avoid swamping the postmaster with signals, we issue the autovac
         * request only once per 64K transaction starts.  This still gives
         * plenty of chances before we get into real trouble.
         * 爲了避免信號淹沒postmaster,我們每64K事務開始時只發出一次autovac請求。
         * 在我們陷入真正的麻煩之前,這仍然給了我們很多解決問題的機會。
         */
        if (IsUnderPostmaster && (xid % 65536) == 0)
            //每隔64K發一次
            SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);

        if (IsUnderPostmaster &&
            TransactionIdFollowsOrEquals(xid, xidStopLimit))
        {
            //xid >= ShmemVariableCache->xidStopLimit
            char       *oldest_datname = get_database_name(oldest_datoid);

            /* complain even if that DB has disappeared */
            //就算DB已消失,也要不停的警告:(
            if (oldest_datname)
                ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                         errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
                                oldest_datname),
                         errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
                                 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
            else
                ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                         errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
                                oldest_datoid),
                         errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
                                 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
        }
        else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
        {
            ////xid >= ShmemVariableCache->xidWarnLimit
            char       *oldest_datname = get_database_name(oldest_datoid);

            /* complain even if that DB has disappeared */
            if (oldest_datname)
                ereport(WARNING,
                        (errmsg("database \"%s\" must be vacuumed within %u transactions",
                                oldest_datname,
                                xidWrapLimit - xid),
                         errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
                                 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
            else
                ereport(WARNING,
                        (errmsg("database with OID %u must be vacuumed within %u transactions",
                                oldest_datoid,
                                xidWrapLimit - xid),
                         errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
                                 "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
        }

        /* Re-acquire lock and start over */
        //重新獲取鎖並啓動
        LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
        xid = ShmemVariableCache->nextXid;
    }

    /*
     * If we are allocating the first XID of a new page of the commit log,
     * zero out that commit-log page before returning. We must do this while
     * holding XidGenLock, else another xact could acquire and commit a later
     * XID before we zero the page.  Fortunately, a page of the commit log
     * holds 32K or more transactions, so we don't have to do this very often.
     * 如果在clog的新page中分配第一個XID,返回前初始化clog page.
     * 必須在持有XidGenLock鎖時執行這個操作,否則的話,
     *   其他事務可能會請求該鎖並在初始化page前提交了一個新事務.
     * 幸運的是,提交日誌的一個頁面包含32K或更多的事務,所以我們不需要經常這樣做。
     *
     * Extend pg_subtrans and pg_commit_ts too.
     * 同時擴展pg_subtrans和pg_commit_ts
     */
    ExtendCLOG(xid);
    ExtendCommitTs(xid);
    ExtendSUBTRANS(xid);

    /*
     * Now advance the nextXid counter.  This must not happen until after we
     * have successfully completed ExtendCLOG() --- if that routine fails, we
     * want the next incoming transaction to try it again.  We cannot assign
     * more XIDs until there is CLOG space for them.
     * 現在可以更新nextXid計數器了.
     * 這必須在我們成功完成ExtendCLOG()之後才能執行——如果該例程失敗,
     *   我們希望下一個進入的事務再次嘗試。
     * 不能分配更多的xid,除非有空閒的CLOG空間。
     */
    //ShmemVariableCache->nextXid ++
    //if ((ShmemVariableCache->nextXid) < FirstNormalTransactionId) 
    //   (ShmemVariableCache->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.
     * 在釋放XidGenLock前,存儲新的XID到共享數據結構ProcArray中.
     * 這可以確保每一個活動的比latestCompletedXid舊的XID都會出現在ProcArray中,
     *   這樣可以減少OldestXmin的跟蹤,請查看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.
     * 要注意的是讀取PGXACT.xid字段時小心只提取一次,
     *   而不是假定可以多次讀取該值而認爲每次返回的值都一樣.
     * 同時我們假定TransactionId和int 提取/寫入是原子的.
     * 
     * The same comments apply to the subxact xid count and overflow fields.
     * 對於subxact xid計數器和溢出字段,參見上述註釋.
     *
     * 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.
     * 在該函數中,進入堡壘進行寫可以防止危險的事情出現.
     * 否則其他後臺進程可能會比如同步檢查本進程的subxids信息,但我們不希望它們看到無效的中間狀態,
     *   例如,在數組條目被填充之前增加nxid。
     *
     * 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.
     * 其他讀取nxids的進程應在使用pg_read_barrier()函數讀取xids條目前執行相關操作,
     *   這樣可以確保它們不會讀取到未經初始化的數組條目,查看src/backend/storage/lmgr/README.barrier說明.
     *
     * 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.)
     * 如果沒有空間將子事務XID放入PGPROC中,則設置cache-overflow標誌。
     * 這可以強制readers查看pg_subtrans以將子事務xid映射到頂層xid。
     * 有一個race-condition窗口,在它的父鏈接被放入pg_subtrans之前,新的XID不會顯示爲正在運行.
     * 然而,在都有可能有理由查詢XID的狀態之前,這種情況就會發生,所以看起來是可以的。
     * (在這個窗口中拍攝的快照*將*包含父XID,因此當稍後有進程查詢時,它們將提供正確的答案。)
     */
    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;
    }
    //釋放鎖
    LWLockRelease(XidGenLock);

    return xid;
}
 

三、跟蹤分析

執行txid_current,觸發函數調用

14:26:26 (xdb@[local]:5432)testdb=#  begin;
BEGIN
14: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();

啓動gdb,設置斷點

(gdb) b GetNewTransactionId
Breakpoint 6 at 0x545e80: file varsup.c, line 56.
(gdb) c
Continuing.

Breakpoint 6, GetNewTransactionId (isSubXact=false) at varsup.c:56
56      if (IsInParallelMode())
(gdb) 

查看調用棧

(gdb) bt
#0  GetNewTransactionId (isSubXact=false) at varsup.c:56
#1  0x0000000000546bd9 in AssignTransactionId (s=0xf9c720 <TopTransactionStateData>) 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 <return> to continue, or q <return> 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) 

獲取XidGenLock鎖

(gdb) n
63      if (IsBootstrapProcessingMode())
(gdb) 
71      if (RecoveryInProgress())
(gdb) 
74      LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
(gdb) 

獲取共享內存變量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) 

擴展clog

(gdb) n
171     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

獲取進程和事務信息

(gdb) n
223         volatile PGXACT *mypgxact = MyPgXact;
(gdb) 
225         if (!isSubXact)
(gdb) 
226             mypgxact->xid = xid;
(gdb) 

釋放鎖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}

返回xid(2409),完成調用.

(gdb) n
243     return xid;
(gdb) 
244 }
(gdb) 
AssignTransactionId (s=0xf9c720 <TopTransactionStateData>) at xact.c:558
558     if (!isSubXact)
(gdb) 

DONE!

四、參考資料

PG Source Code

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章