使用eclipse分析postgres事務的調用開始過程

      postgres數據庫是開源數據庫,數據庫底層是通過C語言來實現的。這裏介紹使用eclipse的debug方式來動態跟蹤postgres中一個事務調用的開始過程(StartTransactionCommand)。

/* ----------------------------------------------------------------
* PostgresMain
*         postgres main loop -- all backends, interactive or otherwise start here
*
* argc/argv are the command line arguments to be used.  (When being forked
* by the postmaster, these are not the original argv array of the process.)
* dbname is the name of the database to connect to, or NULL if the database
* name should be extracted from the command line arguments or defaulted.
* username is the PostgreSQL user name to be used for the session.
* ----------------------------------------------------------------
*/
void
PostgresMain(int argc, char *argv[],
                         const char *dbname,
                         const char *username)
{
.
.--//中間略
.
                switch (firstchar)
                {      
                        case 'Q':                       /* simple query */
                                {      
                                        const char *query_string;

                                        /* Set statement_timestamp() */
                                        SetCurrentStatementStartTimestamp();

                                        query_string = pq_getmsgstring(&input_message);
                                        pq_getmsgend(&input_message);

                                        if (am_walsender)
                                        {      
                                                if (!exec_replication_command(query_string))
                                                        exec_simple_query(query_string);
                                        }      
                                        else   
                                                exec_simple_query(query_string);
.
.--//中間略
.
}

--//中間有一個exec_simple_query函數調用

--//開啓一個會話

--//使用eclipse調試debug6561進程號

 --//4179行和4182行添加斷點

--//6561會話執行一條insert 語句

--//查看eclipse界面

--//查看exec_simple_query函數

/*
* exec_simple_query
*
* Execute a "simple Query" protocol message.
*/
static void
exec_simple_query(const char *query_string)
{
.
.--//中間略
.
   /*
         * Start up a transaction command.  All queries generated by the
         * query_string will be in this same command block, *unless* we find a
         * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after
         * one of those, else bad things will happen in xact.c. (Note that this
         * will normally change current memory context.)
         */
        start_xact_command();     //調用start_xact_command啓動一個事務
.
.--//中間略
.
}

--//exec_simple_query中調用了start_xact_command();函數
--//在start_xact_command 函數上加斷點

--//查看start_xact_command函數

/*
* Convenience routines for starting/committing a single command.
*/
static void
start_xact_command(void)
{
        if (!xact_started)
        {
                StartTransactionCommand();   //--調用StartTransactionCommand函數啓動一個事務

                xact_started = true;
        }

        /*
         * Start statement timeout if necessary.  Note that this'll intentionally
         * not reset the clock on an already started timeout, to avoid the timing
         * overhead when start_xact_command() is invoked repeatedly, without an
         * interceding finish_xact_command() (e.g. parse/bind/execute).  If that's
         * not desired, the timeout has to be disabled explicitly.
         */
        enable_statement_timeout();
}

--//在StartTransactionCommand()處加上斷點

--//查看StartTransactionCommand()函數

/*
*      StartTransactionCommand
*/
void
StartTransactionCommand(void)
{
        //--初始化事物信息(具體初始化可以參考)
        TransactionState s = CurrentTransactionState;

        switch (s->blockState)
        {
                        /*
                         * if we aren't in a transaction block, we just do our usual start
                         * transaction.
                         */
                case TBLOCK_DEFAULT:
                        StartTransaction();             --//調用啓動事務函數
                        s->blockState = TBLOCK_STARTED;
                        break;

                        /*
                         * We are somewhere in a transaction block or subtransaction and
                         * about to start a new command.  For now we do nothing, but
                         * someday we may do command-local resource initialization. (Note
                         * that any needed CommandCounterIncrement was done by the
                         * previous CommitTransactionCommand.)
                         */
                case TBLOCK_INPROGRESS:
                case TBLOCK_IMPLICIT_INPROGRESS:
                case TBLOCK_SUBINPROGRESS:
                        break;

                        /*
                         * Here we are in a failed transaction block (one of the commands
                         * caused an abort) so we do nothing but remain in the abort
                         * state.  Eventually we will get a ROLLBACK command which will
                         * get us out of this state.  (It is up to other code to ensure
                         * that no commands other than ROLLBACK will be processed in these
                         * states.)
                         */
                case TBLOCK_ABORT:
                case TBLOCK_SUBABORT:
                        break;

                        /* These cases are invalid. */
                case TBLOCK_STARTED:
                case TBLOCK_BEGIN:
                case TBLOCK_PARALLEL_INPROGRESS:
                case TBLOCK_SUBBEGIN:
                case TBLOCK_END:
                case TBLOCK_SUBRELEASE:
                case TBLOCK_SUBCOMMIT:
                case TBLOCK_ABORT_END:
                case TBLOCK_SUBABORT_END:
                case TBLOCK_ABORT_PENDING:
                case TBLOCK_SUBABORT_PENDING:
                case TBLOCK_SUBRESTART:
                case TBLOCK_SUBABORT_RESTART:
                case TBLOCK_PREPARE:
                        elog(ERROR, "StartTransactionCommand: unexpected state %s",
                                 BlockStateAsString(s->blockState));
                        break;
        }

        /*
         * We must switch to CurTransactionContext before returning. This is
         * already done if we called StartTransaction, otherwise not.
         */
        Assert(CurTransactionContext != NULL);
        MemoryContextSwitchTo(CurTransactionContext);   --//設置事務上下文信息
}

--//其中TransactionState是TransactionStateData類型的結構體指針
--//定義如下
--//typedef TransactionStateData *TransactionState;
--//位置如下
[root@postgres backend]# grep -nir 'typedef TransactionStateData' ./
./access/transam/xact.c:194:typedef TransactionStateData *TransactionState;
[root@postgres backend]# 
--//TransactionStateData爲一個結構體

文件./access/transam/xact.c
/*
*      transaction state structure
*/
typedef struct TransactionStateData
{
        TransactionId transactionId;    /* my XID, or Invalid if none */
        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 */
        int                     gucNestLevel;   /* GUC context nesting depth */
        MemoryContext curTransactionContext;    /* my xact-lifetime context */
        ResourceOwner curTransactionOwner;      /* my query resources */
        TransactionId *childXids;       /* subcommitted child XIDs, in XID order */
        int                     nChildXids;             /* # of subcommitted child XIDs */
        int                     maxChildXids;   /* allocated size of childXids[] */
        Oid                     prevUser;               /* previous CurrentUserId setting */
        int                     prevSecContext; /* previous SecurityRestrictionContext */
        bool            prevXactReadOnly;       /* entry-time xact r/o state */
        bool            startedInRecovery;      /* did we start in recovery? */
        bool            didLogXid;              /* has xid been included in WAL record? */
        int                     parallelModeLevel;      /* Enter/ExitParallelMode counter */
        struct TransactionStateData *parent;    /* back link to parent */
} TransactionStateData;

--//CurrentTransactionState爲一個已經初始化的結構體
static TransactionState CurrentTransactionState = &TopTransactionStateData;

--//TopTransactionStateData的定義爲

/*
* CurrentTransactionState always points to the current transaction state
* block.  It will point to TopTransactionStateData when not in a
* transaction at all, or when in a top-level transaction.
*/
static TransactionStateData TopTransactionStateData = {
        0,                                                      /* transaction id */
        0,                                                      /* subtransaction id */
        NULL,                                           /* savepoint name */
        0,                                                      /* savepoint level */
        TRANS_DEFAULT,                          /* transaction state */
        TBLOCK_DEFAULT,                         /* transaction block state from the client
                                                                 * perspective */
        0,                                                      /* transaction nesting depth */
        0,                                                      /* GUC context nesting depth */
        NULL,                                           /* cur transaction context */
        NULL,                                           /* cur transaction resource owner */
        NULL,                                           /* subcommitted child Xids */
        0,                                                      /* # of subcommitted child Xids */
        0,                                                      /* allocated size of childXids[] */
        InvalidOid,                                     /* previous CurrentUserId setting */
        0,                                                      /* previous SecurityRestrictionContext */
        false,                                          /* entry-time xact r/o state */
        false,                                          /* startedInRecovery */
        false,                                          /* didLogXid */
        0,                                                      /* parallelModeLevel */
        NULL                                            /* link to parent state block */
};

--//StartTransaction()上設置斷點

--//查看此時的變量信息,這些信息正是變量TopTransactionStateData的值

--//查看StartTransaction()

/*
*      StartTransaction
*/
static void
StartTransaction(void)
{
        TransactionState s;             //--定義TransactionState變量
        VirtualTransactionId vxid;

        /*
         * Let's just make sure the state stack is empty
         */
        s = &TopTransactionStateData;     //--初始化爲TopTransactionStateData結構體的值
        CurrentTransactionState = s;          //--賦值給CurrentTransactionState變量

        Assert(XactTopTransactionId == InvalidTransactionId);

        /*
         * check the current transaction state
         */
        if (s->state != TRANS_DEFAULT)
                elog(WARNING, "StartTransaction while in %s state",
                         TransStateAsString(s->state));

        /*
         * set the current transaction state information appropriately during
         * start processing
         * 在啓動處理期間適當地設置當前事務狀態信息
         */
        s->state = TRANS_START;
        s->transactionId = InvalidTransactionId;        /* until assigned 直到分配 */

        /*
         * Make sure we've reset xact state variables
         *
         * If recovery is still in progress, mark this transaction as read-only.
         * We have lower level defences in XLogInsert and elsewhere to stop us
         * from modifying data during recovery, but this gives the normal
         * indication to the user that the transaction is read-only.
         * 確保我們已經重置了xact狀態變量,如果恢復仍在進行中,將該事務標記爲只讀。我們在XLogInsert和其他地方有較低級別的防禦機制來阻止我們在恢復期* 間修改數據,但是這向用戶提供了事務是隻讀的正常指示。
         */
        if (RecoveryInProgress())
        {
                s->startedInRecovery = true;
                XactReadOnly = true;
        }
        else
        {
                s->startedInRecovery = false;
                XactReadOnly = DefaultXactReadOnly;
        }
        XactDeferrable = DefaultXactDeferrable;  //DefaultXactDeferrable=false
        XactIsoLevel = DefaultXactIsoLevel;      //DefaultXactIsoLevel = XACT_READ_COMMITTED
        forceSyncCommit = false;
        MyXactFlags = 0;

        /*
         * reinitialize within-transaction counters   重新啓動within-transaction計數器
         */
        s->subTransactionId = TopSubTransactionId;    //TopSubTransactionId=1
        currentSubTransactionId = TopSubTransactionId; //TopSubTransactionId=1
        currentCommandId = FirstCommandId;     //FirstCommandId=0
        currentCommandIdUsed = false;

        /*
         * initialize reported xid accounting   初始化報告的xid
         */
        nUnreportedXids = 0;
        s->didLogXid = false;   //*has xid been included in WAL record (xid是否包含在WAL record中)

        /*
         * must initialize resource-management stuff first   必須先初始化資源管理
         */
         //創建事物上下文
        AtStart_Memory();
        //設置事物owner
        AtStart_ResourceOwner();

        /*
         * Assign a new LocalTransactionId, and combine it with the backendId to
         * form a virtual transaction id.
         * 分配一個新的LocalTransactionId,並將其與backendId組合起來形成一個虛擬事務id。
         */
        vxid.backendId = MyBackendId;
        vxid.localTransactionId = GetNextLocalTransactionId();

        /*
         * Lock the virtual transaction id before we announce it in the proc array
         * 在將虛擬事務id聲明到proc數組之前鎖定它
         */
        VirtualXactLockTableInsert(vxid);

        /*
         * Advertise it in the proc array.  We assume assignment of
         * LocalTransactionID is atomic, and the backendId should be set already.
         * 在proc數組中發佈它。我們假設LocalTransactionID的賦值是原子的,並且應該已經設置了backendId。
         */
        Assert(MyProc->backendId == vxid.backendId);
        MyProc->lxid = vxid.localTransactionId;

        TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);

        /*
         * set transaction_timestamp() (a/k/a now()).  Normally, we want this to
         * be the same as the first command's statement_timestamp(), so don't do a
         * fresh GetCurrentTimestamp() call (which'd be expensive anyway).  But
         * for transactions started inside procedures (i.e., nonatomic SPI
         * contexts), we do need to advance the timestamp.  Also, in a parallel
         * worker, the timestamp should already have been provided by a call to
         * SetParallelStartTimestamps().
         */
        if (!IsParallelWorker())
        {
                if (!SPI_inside_nonatomic_context())
                        xactStartTimestamp = stmtStartTimestamp;
                else
                        xactStartTimestamp = GetCurrentTimestamp();
        }
        else
                Assert(xactStartTimestamp != 0);
        pgstat_report_xact_timestamp(xactStartTimestamp);
        /* Mark xactStopTimestamp as unset. */
        xactStopTimestamp = 0;

        /*
         * initialize current transaction state fields
         *
         * note: prevXactReadOnly is not used at the outermost level
         */
        s->nestingLevel = 1;
        s->gucNestLevel = 1;
        s->childXids = NULL;
        s->nChildXids = 0;
        s->maxChildXids = 0;
        GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
        /* SecurityRestrictionContext should never be set outside a transaction */
        Assert(s->prevSecContext == 0);

        /*
         * initialize other subsystems for new transaction
         */
        AtStart_GUC();      //--
        AtStart_Cache();
        AfterTriggerBeginXact();

        /*
         * done with start processing, set current transaction state to "in
         * progress"
         */
        s->state = TRANS_INPROGRESS;

        ShowTransactionState("StartTransaction");
}

--//進入StartTransaction函數單步調試

--//查看ShowTransactionState函數

/*
* ShowTransactionState
*              Debug support
*/
static void
ShowTransactionState(const char *str)
{
        /* skip work if message will definitely not be printed */
        if (log_min_messages <= DEBUG5 || client_min_messages <= DEBUG5)
                ShowTransactionStateRec(str, CurrentTransactionState);
}

--//查看ShowTransactionStateRec函數
/*
* ShowTransactionStateRec
*              Recursive subroutine for ShowTransactionState
*/
static void
ShowTransactionStateRec(const char *str, TransactionState s)
{
        StringInfoData buf;

        initStringInfo(&buf);

        if (s->nChildXids > 0)
        {
                int                     i;

                appendStringInfo(&buf, ", children: %u", s->childXids[0]);
                for (i = 1; i < s->nChildXids; i++)
                        appendStringInfo(&buf, " %u", s->childXids[i]);
        }

        if (s->parent)
                ShowTransactionStateRec(str, s->parent);

        /* use ereport to suppress computation if msg will not be printed */
        ereport(DEBUG5,
                        (errmsg_internal("%s(%d) name: %s; blockState: %s; state: %s, xid/subid/cid: %u/%u/%u%s%s",
                                                         str, s->nestingLevel,
                                                         PointerIsValid(s->name) ? s->name : "unnamed",
                                                         BlockStateAsString(s->blockState),
                                                         TransStateAsString(s->state),
                                                         (unsigned int) s->transactionId,
                                                         (unsigned int) s->subTransactionId,
                                                         (unsigned int) currentCommandId,
                                                         currentCommandIdUsed ? " (used)" : "",
                                                         buf.data)));

        pfree(buf.data);
}

--//report提示打印debug5的信息,目前數據庫日誌級別是配置的debug5

postgres=# show client_min_messages;
DEBUG:  StartTransaction(1) name: unnamed; blockState: DEFAULT; state: INPROGRESS, xid/subid/cid: 0/1/0
DEBUG:  CommitTransaction(1) name: unnamed; blockState: STARTED; state: INPROGRESS, xid/subid/cid: 0/1/0
 client_min_messages 
---------------------
 debug5
(1 row)

postgres=# show log_min_messages;
DEBUG:  StartTransaction(1) name: unnamed; blockState: DEFAULT; state: INPROGRESS, xid/subid/cid: 0/1/0
DEBUG:  CommitTransaction(1) name: unnamed; blockState: STARTED; state: INPROGRESS, xid/subid/cid: 0/1/0
 log_min_messages 
------------------
 debug5
(1 row)

postgres=# 

--//查看會話信息已經和eclipse看到的同步,到了ereport(DEBUG5..)

       上面使用eclipse演示了postgres中事物開始的一個過程,通過eclipse加斷點的方式可以一步步的跟蹤函數調用,這樣對於源碼的學習會更方便。上面的演示中insert語句並沒有完成,因爲這裏還有好多函數沒有判斷完,比如enable_statement_timeout()、CommitTransactionCommand()等相關的函數調用在這裏沒有演示。其中在StartTransaction()函數中的AtStart_Memory()函數是關於postgres的MemoryContext內存上下文的申請和釋放問題,其過程也相當複雜。具體分析可以參考鏈接:https://blog.csdn.net/m15217321304/article/details/105476959

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