首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(225)- Transaction(子事务处理)

PostgreSQL 源码解读(225)- Transaction(子事务处理)

翻译 PostgreSQL 作者:husthxd 时间:2019-09-10 11:51:08 0 删除 编辑

本节是PostgreSQL 事务管理中的关于子事务部分的说明,翻译自README文件.

一、Subtransaction Handling


Subtransaction Handling
Subtransactions are implemented using a stack of TransactionState structures,
each of which has a pointer to its parent transaction's struct.  When a new
subtransaction is to be opened, PushTransaction is called, which creates a new
TransactionState, with its parent link pointing to the current transaction.
StartSubTransaction is in charge of initializing the new TransactionState to
sane values, and properly initializing other subsystems (AtSubStart routines).
When closing a subtransaction, either CommitSubTransaction has to be called
(if the subtransaction is committing), or AbortSubTransaction and
CleanupSubTransaction (if it's aborting).  In either case, PopTransaction is
called so the system returns to the parent transaction.
One important point regarding subtransaction handling is that several may need
to be closed in response to a single user command.  That's because savepoints
have names, and we allow to commit or rollback a savepoint by name, which is
not necessarily the one that was last opened.  Also a COMMIT or ROLLBACK
command must be able to close out the entire stack.  We handle this by having
the utility command subroutine mark all the state stack entries as commit-
pending or abort-pending, and then when the main loop reaches
CommitTransactionCommand, the real work is done.  The main point of doing
things this way is that if we get an error while popping state stack entries,
the remaining stack entries still show what we need to do to finish up.
In the case of ROLLBACK TO <savepoint>, we abort all the subtransactions up
through the one identified by the savepoint name, and then re-create that
subtransaction level with the same name.  So it's a completely new
subtransaction as far as the internals are concerned.
在ROLLBACK TO <savepoint>这种情况中,我们取消了从savepoint name到当前的所有子事务,
Other subsystems are allowed to start "internal" subtransactions, which are
handled by BeginInternalSubTransaction.  This is to allow implementing
exception handling, e.g. in PL/pgSQL.  ReleaseCurrentSubTransaction and
RollbackAndReleaseCurrentSubTransaction allows the subsystem to close said
subtransactions.  The main difference between this and the savepoint/release
path is that we execute the complete state transition immediately in each
subroutine, rather than deferring some work until CommitTransactionCommand.
Another difference is that BeginInternalSubTransaction is allowed when no
explicit transaction block has been established, while DefineSavepoint is not.
Transaction and Subtransaction Numbering
Transactions and subtransactions are assigned permanent XIDs only when/if
they first do something that requires one --- typically, insert/update/delete
a tuple, though there are a few other places that need an XID assigned.
If a subtransaction requires an XID, we always first assign one to its
parent.  This maintains the invariant that child transactions have XIDs later
than their parents, which is assumed in a number of places.
The subsidiary actions of obtaining a lock on the XID and entering it into
pg_subtrans and PG_PROC are done at the time it is assigned.
A transaction that has no XID still needs to be identified for various
purposes, notably holding locks.  For this purpose we assign a "virtual
transaction ID" or VXID to each top-level transaction.  VXIDs are formed from
two fields, the backendID and a backend-local counter; this arrangement allows
assignment of a new VXID at transaction start without any contention for
shared memory.  To ensure that a VXID isn't re-used too soon after backend
exit, we store the last local counter value into shared memory at backend
exit, and initialize it from the previous value for the same backendID slot
at backend start.  All these counters go back to zero at shared memory
re-initialization, but that's OK because VXIDs never appear anywhere on-disk.
Internally, a backend needs a way to identify subtransactions whether or not
they have XIDs; but this need only lasts as long as the parent top transaction
endures.  Therefore, we have SubTransactionId, which is somewhat like
CommandId in that it's generated from a counter that we reset at the start of
each top transaction.  The top-level transaction itself has SubTransactionId 1,
and subtransactions have IDs 2 and up.  (Zero is reserved for
InvalidSubTransactionId.)  Note that subtransactions do not have their
own VXIDs; they use the parent top transaction's VXID.


 *    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.
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 */
    TBLOCK_END,                    /* COMMIT received */
    TBLOCK_ABORT,                /* failed xact, awaiting ROLLBACK */
    TBLOCK_ABORT_END,            /* failed xact, ROLLBACK received */
    TBLOCK_ABORT_PENDING,        /* live xact, ROLLBACK received */
    TBLOCK_PREPARE,                /* live xact, PREPARE received */
    /* subtransaction states */
    TBLOCK_SUBBEGIN,            /* starting a subtransaction */
    TBLOCK_SUBINPROGRESS,        /* live subtransaction */
    TBLOCK_SUBRELEASE,            /* RELEASE received */
    TBLOCK_SUBCOMMIT,            /* COMMIT received while TBLOCK_SUBINPROGRESS */
    TBLOCK_SUBABORT,            /* failed subxact, awaiting ROLLBACK */
    TBLOCK_SUBABORT_END,        /* failed subxact, ROLLBACK received */
    TBLOCK_SUBABORT_PENDING,    /* live subxact, ROLLBACK received */
    TBLOCK_SUBRESTART,            /* live subxact, ROLLBACK TO received */
    TBLOCK_SUBABORT_RESTART        /* failed subxact, ROLLBACK TO received */
} TBlockState;
 *    transaction state structure
typedef struct TransactionStateData
    FullTransactionId fullTransactionId;    /* my FullTransactionId */
    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 */
    bool        chain;            /* start a new block after this one */
    struct TransactionStateData *parent;    /* back link to parent */
} TransactionStateData;
typedef TransactionStateData *TransactionState;



来自 “ ITPUB博客 ” ,链接:,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录


  • 博文量
  • 访问量