Improve comments in GTM code, minor naming tweaks
authorTomas Vondra <[email protected]>
Sat, 4 Nov 2017 15:49:11 +0000 (16:49 +0100)
committerTomas Vondra <[email protected]>
Sat, 4 Nov 2017 15:50:57 +0000 (16:50 +0100)
This patch improves comments in gtm_txn.c and gtm_snap.c in three
basic ways:

1) Adds global comments explaining the basics of transaction and
   snapshot management APIs - underlying concepts, main methods.

2) Improves (and adds) function-level comments, explaining the
   meaning of parameters, return values, and other details.

3) Tweaks the naming of several API functions, to make them more
   consistent with the rest of the module.

src/gtm/main/gtm_thread.c
src/gtm/main/gtm_txn.c
src/gtm/main/main.c
src/include/gtm/gtm_txn.h

index 462a2a85170b320017cbe73d582ef04eb2efa739..151e61add026289564607bde63988793ed7292ba 100644 (file)
@@ -432,7 +432,7 @@ void
 GTM_SetInitialAndNextClientIdentifierAtPromote(void)
 {
        GTM_RWLockAcquire(&GTMThreads->gt_lock, GTM_LOCKMODE_WRITE);
-       GTMThreads->gt_starting_client_id = GTMGetLastClientIdentifier();
+       GTMThreads->gt_starting_client_id = GTM_GetLastClientIdentifier();
        GTMThreads->gt_next_client_id =
                GTM_CLIENT_ID_NEXT(GTMThreads->gt_starting_client_id);
        GTM_RWLockRelease(&GTMThreads->gt_lock);
index 1fde21aff8fee767bed1e13608a195b3bfad74a7..62410e14661de8bbe8765ef37e220d664263e74a 100644 (file)
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * gtm_txn.c
- *     Transaction handling
+ *     Transaction handling on GTM
  *
  * Portions Copyright (c) 2012-2014, TransLattice, Inc.
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL$
+ *       src/gtm/main/gtm_txn.c
+ *
+ *
+ * Functions in this file manage the main transaction array (GTMTransactions)
+ * and provide API to manage the contents - begin, commit and/or abort global
+ * transactions.
+ *
+ * The rest of this comment is a brief overview of the API. It's by no means
+ * exhaustive - you can find more details in comments at each function or
+ * in the code itself. But it should explain basic concepts and main functions
+ * of the GTM Transaction API.
+ *
+ * There are additional parts of the GTM, dealing with other types of objects
+ * (e.g. sequences or snapshots). Those are managed by functions in other
+ * files, and you will need to look into those files for description of that
+ * part of the API.
+ *
+ *
+ * Transaction Identifiers
+ * -----------------------
+ * There are several ways to identify a global transaction. Some identifiers
+ * are internal, while other are meant as an interface with users. There are
+ * four main types of identifiers in the code:
+ *
+ * 1) GTM_TransactionHandle (handle) : Index into the internal array of global
+ *    transactions (GTMTransactions.gt_transactions_array), so the values are
+ *    limited to interval [0, GTM_MAX_GLOBAL_TRANSACTIONS].
+ *
+ * 2) GlobalTransactionId (GXID) : Sequential ID (uint32), assigned by GTM
+ *    to a transaction, just like PostgreSQL assigns XIDs to local transactions.
+ *
+ * 3) Global Transaction Identifier (GID) : Assigned to transactionss in 2PC
+ *    transactions, visible to users.
+ *
+ * 4) Global Session ID : Not really a transaction identifier, but it's often
+ *    necessary to lookup transaction assigned to a global session.
+ *
+ * One difference between the identifiers is in the cost of looking-up the
+ * transaction. Handles are very cheap, as all that's needed is simply
+ *
+ *     GTMTransactions.gt_transactions_array[handle]
+ *
+ * All other identifiers may require walking through the currently opened
+ * transactions, which is more expensive. This is why the API references to
+ * transactions by handles in most places, and provides functions to convert
+ * the other identifiers to handles:
+ *
+ *     - GTM_GXIDToHandle()            : GXID       -> handle
+ *     - GTM_GlobalSessionIDToHandle() : session ID -> handle
+ *     - GTM_GIDToHandle()             : GID        -> handle
+ *
+ * Conversion in the other direction is trivial, as the identifiers are
+ * stored as fields in GTM_TransactionInfo.
+ *
+ *
+ * Transaction Management
+ * ----------------------
+ * The basic transaction management commands (BEGIN/PREPARE/COMMIT/ABORT)
+ * are implemented in these eight methods:
+ *
+ *     - GTM_BeginTransaction()
+ *     - GTM_BeginTransactionMulti()
+ *
+ *     - GTM_RollbackTransaction()
+ *     - GTM_RollbackTransactionMulti()
+ *
+ *     - GTM_CommitTransaction()
+ *     - GTM_CommitTransactionMulti()
+ *
+ *     - GTM_StartPreparedTransaction()
+ *     - GTM_PrepareTransaction()
+ *
+ * The first three commands have two variants - the first one processes a
+ * single transaction (handle), while the "multi" variant operates on an
+ * array of handles. This is useful when processing commands grouped by
+ * GTM proxy nodes.
+ *
+ *
+ * Message Processing
+ * ------------------
+ * Most of the transaction management methods are declared as static, and
+ * are invoked from functions processing messages arriving from clients
+ * over the network. Names of all these meethods start with "Process", and
+ * in most cases it's quite clear which transaction management command is
+ * invoked by each function:
+ *
+ *     - ProcessBeginTransactionCommand()
+ *     - ProcessBeginTransactionGetGXIDCommand()
+ *     - ProcessBeginTransactionGetGXIDAutovacuumCommand()
+ *     - ProcessBeginTransactionGetGXIDCommandMulti()
+ *
+ *     - ProcessRollbackTransactionCommand()
+ *     - ProcessRollbackTransactionCommandMulti()
+ *
+ *     - ProcessCommitTransactionCommand()
+ *     - ProcessCommitTransactionCommandMulti()
+ *     - ProcessCommitPreparedTransactionCommand()
+ *
+ *     - ProcessPrepareTransactionCommand()
+ *     - ProcessStartPreparedTransactionCommand()
+ *
+ * These function handle communication not only with the GTM clients (that
+ * is backends on datanodes/coordinators or proxies), but with a GTM standby
+ * nodes. They typically receive a message, execute the command locally
+ * and also forward it to the GTM standby node before responding to client.
+ *
+ * For some methods there are special variants with "Bkup" in the name:
+ *
+ *     - ProcessBkupBeginTransactionCommand()
+ *     - ProcessBkupBeginTransactionGetGXIDCommand()
+ *     - ProcessBkupBeginTransactionGetGXIDAutovacuumCommand()
+ *     - ProcessBkupBeginTransactionGetGXIDCommandMulti()
+ *
+ * Those are handling the commands on standby, in a slightly different way
+ * (e.g. without forwarding the messages to GTM standby nodes, etc.).
  *
  *-------------------------------------------------------------------------
  */
 
 extern bool Backup_synchronously;
 
-#define GTM_CONTROL_VERSION    20160302
-
 /* Local functions */
 static bool GTM_SetDoVacuum(GTM_TransactionHandle handle);
-static void init_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo,
+static void GTM_TransactionInfo_Init(GTM_TransactionInfo *gtm_txninfo,
                                                                         GTM_TransactionHandle txn,
                                                                         GTM_IsolationLevel isolevel,
                                                                         uint32 client_id,
                                                                         GTMProxy_ConnID connid,
                                                                         const char *global_sessionid,
                                                                         bool readonly);
-static void clean_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo);
+static void GTM_TransactionInfo_Clean(GTM_TransactionInfo *gtm_txninfo);
 static GTM_TransactionHandle GTM_GlobalSessionIDToHandle(
                                                                        const char *global_sessionid);
 static bool GTM_NeedXidRestoreUpdate(void);
-static int GTM_CommitTransaction(GTM_TransactionHandle txn,
-               int waited_xid_count, GlobalTransactionId *waited_xids);
 
 GlobalTransactionId ControlXid;  /* last one written to control file */
 GTM_Transactions GTMTransactions;
 
+/*
+ * GTM_InitTxnManager
+ *     Initializes the internal data structures used by GTM.
+ *
+ * This only resets the data structures to "empty" state, initialized the
+ * locks protecting the structures. Restoring the last values from the GTM
+ * control file (written on shutdown) is handled elsewhere.
+ */
 void
 GTM_InitTxnManager(void)
 {
@@ -69,12 +187,12 @@ GTM_InitTxnManager(void)
 
        /*
         * XXX When GTM is stopped and restarted, it must start assinging GXIDs
-        * greater than the previously assgined values. If it was a clean shutdown,
+        * greater than the previously assigned values. If it was a clean shutdown,
         * the GTM can store the last assigned value at a known location on
         * permanent storage and read it back when it's restarted. It will get
         * trickier for GTM failures.
         *
-        * TODO We skip this part for the prototype.
+        * Restarts after a clean shutdown is handled by GTM_RestoreTxnInfo.
         */
        GTMTransactions.gt_nextXid = FirstNormalGlobalTransactionId;
 
@@ -120,9 +238,15 @@ GTM_InitTxnManager(void)
        return;
 }
 
-
 /*
- * Given the GXID, find the corresponding transaction handle.
+ * GTM_GXIDToHandle_Internal
+ *             Given the GXID, find handle of the corresponding global transaction.
+ *
+ * We simply walk the list of open transactions until we find a match.
+ *
+ * XXX I wonder if this might be an issue, as the search is linear and we
+ * may have up to 16k global transactions (by default). In that case we
+ * should change this to use a hash table (or so) to speed the lookup.
  */
 static GTM_TransactionHandle
 GTM_GXIDToHandle_Internal(GlobalTransactionId gxid, bool warn)
@@ -157,12 +281,30 @@ GTM_GXIDToHandle_Internal(GlobalTransactionId gxid, bool warn)
        }
 }
 
+/*
+ * GTM_GXIDToHandle
+ *             Given the GXID, find handle of the corresponding global transaction.
+ *
+ * If the GXID is not found, returns InvalidTransactionHandle (and emits a
+ * warning).
+ */
 GTM_TransactionHandle
 GTM_GXIDToHandle(GlobalTransactionId gxid)
 {
        return GTM_GXIDToHandle_Internal(gxid, true);
 }
 
+/*
+ * GTM_GlobalSessionIDToHandle
+ *             Given ID of a global session, find ID of the global transaction.
+ *
+ * Returns InvalidTransactionHandle for empty session ID (NULL or '\0'),
+ * as well as for unknown session IDs.
+ *
+ * XXX Similarly to GTM_GXIDToHandle_Internal, the search is simply a loop
+ * over gt_open_transactions, so it might be causing performance issues.
+ * Especially as this is used in GTM_BeginTransactionMulti.
+ */
 static GTM_TransactionHandle
 GTM_GlobalSessionIDToHandle(const char *global_sessionid)
 {
@@ -185,6 +327,13 @@ GTM_GlobalSessionIDToHandle(const char *global_sessionid)
        return InvalidTransactionHandle;
 }
 
+/*
+ * GTM_IsGXIDInProgress
+ *     Determines if a global transaction with a given GXID is still in progress.
+ *
+ * Returns true when the GXID is still in progress (exists in gt_open_transactions),
+ * false otherwise.
+ */
 static bool
 GTM_IsGXIDInProgress(GlobalTransactionId gxid)
 {
@@ -192,11 +341,14 @@ GTM_IsGXIDInProgress(GlobalTransactionId gxid)
                        InvalidTransactionHandle);
 }
 /*
- * Given the GID (for a prepared transaction), find the corresponding
- * transaction handle.
+ * GTM_GIDToHandle
+ *             Find transaction handle for a given the GID (prepared transaction).
+ *
+ * XXX Similarly to GTM_GXIDToHandle_Internal the search is simply a loop
+ * over gt_open_transactions, so might be subject performance issues.
  */
 static GTM_TransactionHandle
-GTM_GIDToHandle(char *gid)
+GTM_GIDToHandle(const char *gid)
 {
        gtm_ListCell *elem = NULL;
        GTM_TransactionInfo *gtm_txninfo = NULL;
@@ -215,18 +367,26 @@ GTM_GIDToHandle(char *gid)
 
        if (gtm_txninfo != NULL)
                return gtm_txninfo->gti_handle;
-       else
-               return InvalidTransactionHandle;
+
+       /* Print warning for unknown global session IDs. */
+       ereport(WARNING,
+               (ERANGE, errmsg("No transaction handle for prepared transaction ID: '%s'",
+                                               gid)));
+
+       return InvalidTransactionHandle;
 }
 
 
 /*
- * Given the transaction handle, find the corresponding transaction info
- * structure
+ * GTM_HandleToTransactionInfo
+ *             Given a transaction handle, find the transaction info structure.
+ *
+ * The transaction is expected to be still in use, so we emit a WARNING if
+ * that's not the case.
  *
  * Note: Since a transaction handle is just an index into the global array,
- * this function should be very quick. We should turn into an inline future for
- * fast path.
+ * this function should be very quick. We should turn into an inline future
+ * for fast path.
  */
 GTM_TransactionInfo *
 GTM_HandleToTransactionInfo(GTM_TransactionHandle handle)
@@ -255,14 +415,20 @@ GTM_HandleToTransactionInfo(GTM_TransactionHandle handle)
 
 
 /*
- * Remove the given transaction info structures from the global array. If the
- * calling thread does not have enough cached structures, we in fact keep the
- * structure in the global array and also add it to the list of cached
+ * GTM_RemoveTransInfoMulti
+ *             Remove multiple transactions from the list of open global transactions.
+ *
+ * If the calling thread does not have enough cached structures, we in fact keep
+ * the structure in the global array and also add it to the list of cached
  * structures for this thread. This ensures that the next transaction starting
  * in this thread can quickly get a free slot in the array of transactions and
  * also avoid repeated malloc/free of the structures.
  *
- * Also compute the latestCompletedXid.
+ * Also updates the gt_latestCompletedXid.
+ *
+ * XXX We seem to be doing a new linear search for each transaction, which seems
+ * rather expensive. We could simply walk gt_open_transactions once and use
+ * gtm_list_delete_cell similarly to GTM_RemoveAllTransInfos.
  */
 static void
 GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count)
@@ -282,6 +448,10 @@ GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count)
 
                GTMTransactions.gt_open_transactions = gtm_list_delete(GTMTransactions.gt_open_transactions, gtm_txninfo[ii]);
 
+               /*
+                * If this transaction is newer than the current gt_latestCompletedXid,
+                * then use the gti_gxid instead.
+                */
                if (GlobalTransactionIdIsNormal(gtm_txninfo[ii]->gti_gxid) &&
                        GlobalTransactionIdFollowsOrEquals(gtm_txninfo[ii]->gti_gxid,
                                                                                           GTMTransactions.gt_latestCompletedXid))
@@ -292,30 +462,42 @@ GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count)
                                gtm_txninfo[ii]->gti_handle);
 
                /*
-                * Now mark the transaction as aborted and mark the structure as not-in-use
+                * Do cleanup of objects (in particular sequences) modified by this
+                * transaction. What exactly happens depends on whether the transaction
+                * committed or aborted.
                 */
-               clean_GTM_TransactionInfo(gtm_txninfo[ii]);
+               GTM_TransactionInfo_Clean(gtm_txninfo[ii]);
        }
 
        GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
-       return;
 }
 
 /*
- * Remove all transaction infos associated with the caller thread and the given
- * backend
+ * GTM_RemoveAllTransInfos
+ *             Remove information about all transactions associated with a client/backend.
+ *
+ * Removes all transactions associated with a specified client/backend from
+ * the global transaction array (GTMTransactions.gt_open_transactions).
+ *
+ * Ignores transactions in GTM_TXN_PREPARED and GTM_TXN_PREPARE_IN_PROGRESS
+ * states - those must not be removed, and will be committed by a different
+ * thread (using a GID).
  *
- * Also compute the latestCompletedXid.
+ * Also updates the gt_latestCompletedXid.
  */
 void
 GTM_RemoveAllTransInfos(uint32 client_id, int backend_id)
 {
        gtm_ListCell *cell, *prev;
 
+       elog(DEBUG1, "GTM_RemoveAllTransInfos: removing transactions for client %u backend %d",
+                                client_id, backend_id);
+
        /*
         * Scan the global list of open transactions
         */
        GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
+
        prev = NULL;
        cell = gtm_list_head(GTMTransactions.gt_open_transactions);
        while (cell != NULL)
@@ -347,7 +529,7 @@ GTM_RemoveAllTransInfos(uint32 client_id, int backend_id)
                        /*
                         * Now mark the transaction as aborted and mark the structure as not-in-use
                         */
-                       clean_GTM_TransactionInfo(gtm_txninfo);
+                       GTM_TransactionInfo_Clean(gtm_txninfo);
 
                        /* move to next cell in the list */
                        if (prev)
@@ -363,18 +545,27 @@ GTM_RemoveAllTransInfos(uint32 client_id, int backend_id)
        }
 
        GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
-       return;
 }
 
 /*
- * Get the latest client identifier issued to the currently open transactions.
+ * GTMGetLastClientIdentifier
+ *             Get the latest client identifier assigned to currently open transactions.
+ *
  * Remember this may not be the latest identifier issued by the old master, but
  * we won't acknowledge client identifiers larger than what we are about to
- * compute. Any such identifiers will be overwritten the new identifier issued
- * by the new master
+ * compute. Any such identifiers will be overwritten new identifiers issued
+ * by the new master.
+ *
+ * XXX Another linear search over gt_open_transactions. Perhaps we could eliminate
+ * most of the searches by updating the value whenever we generate a higher value,
+ * and only doing the search when the client with the highest ID terminates.
+ *
+ * XXX What happens when the value wraps around, which is what GTM_CLIENT_ID_NEXT
+ * apparently does? If we ignore identifiers higher than the value, isn't that an
+ * issue?
  */
 uint32
-GTMGetLastClientIdentifier(void)
+GTM_GetLastClientIdentifier(void)
 {
        gtm_ListCell *cell;
        uint32 last_client_id = 0;
@@ -391,16 +582,25 @@ GTMGetLastClientIdentifier(void)
 
                if (GTM_CLIENT_ID_GT(gtm_txninfo->gti_client_id, last_client_id))
                        last_client_id = gtm_txninfo->gti_client_id;
+
                cell = gtm_lnext(cell);
        }
 
        GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
+
+       elog(DEBUG1, "GTMGetLastClientIdentifier: last client ID %u", last_client_id);
+
        return last_client_id;
 }
 
 /*
- * Set that the transaction is doing vacuum
+ * GTM_SetDoVacuum
+ *             Mark a given transaction (identified by a transaction handle) as VACUUM.
  *
+ * Matters for GTM_GetTransactionSnapshot, which ignores lazy vacuums when
+ * building transaction snapshot
+ *
+ * Fails with an ERROR when the transaction handle does not exist.
  */
 static bool
 GTM_SetDoVacuum(GTM_TransactionHandle handle)
@@ -415,21 +615,53 @@ GTM_SetDoVacuum(GTM_TransactionHandle handle)
 }
 
 /*
- * Allocate the next XID for my new transaction
+ * GTM_GetGlobalTransactionIdMulti
+ *             Allocate GXID for a list of transaction handles.
+ *
+ * The function accepts an array of transaction handles with txn_count elements,
+ * some of which may already have GXID assigned. Such handles (that already had
+ * GXID assigned) are skipped and we don't try to assign a new GXID to them.
+ *
+ * For we handles without a GXID, the function assigns a GXID, and tracks the
+ * handle to new_handles, so that the caller can easily identify which handles
+ * were modified.
  *
- * The new XID is also stored into the transaction info structure of the given
- * transaction before returning.
+ * The output array 'gxids' should contain GXIDs for all handles (even those
+ * that had GXID assigned before calling this function).
+ *
+ * That means both 'gxids' and 'new_handles' should have space for at least
+ * txn_count elements, but 'new_handles' may use only some of the space.
+ *
+ * Input:
+ *             handles                 - transactions to assing GXID to
+ *             txn_count               - number of handles in 'handles' array
+ *
+ * Output:
+ *             gxids                   - array of newly assigned GXIDs
+ *             new_handles             - array of handles with newly assigned GXIDs
+ *             new_txn_count   - number of newly assigned GXIDs (and number of elements
+ *                                             in new_handles)
  */
 static bool
-GTM_GetGlobalTransactionIdMulti(GTM_TransactionHandle handle[], int txn_count,
-               GlobalTransactionId gxid[], GTM_TransactionHandle new_handle[],
+GTM_GetGlobalTransactionIdMulti(GTM_TransactionHandle handles[], int txn_count,
+               GlobalTransactionId gxids[], GTM_TransactionHandle new_handles[],
                int *new_txn_count)
 {
        GlobalTransactionId xid = InvalidGlobalTransactionId;
        GTM_TransactionInfo *gtm_txninfo = NULL;
        int ii;
+       int new_handles_count = 0;
        bool save_control = false;
 
+       elog(DEBUG1, "GTM_GetGlobalTransactionIdMulti: generate GXIDs for %d transactions", txn_count);
+
+       /* gxids is required parameter (we always return the GXID) */
+       Assert(gxids != NULL);
+
+       /* either both new_handles and new_txn_count, or neiter of them */
+       Assert((new_handles && new_txn_count) || (!new_handles && !new_txn_count));
+
+       /* GTM standby can only receive GXID from the GTM master */
        if (Recovery_IsStandby())
        {
                ereport(ERROR, (EINVAL, errmsg("GTM is running in STANDBY mode -- can not issue new transaction ids")));
@@ -445,23 +677,19 @@ GTM_GetGlobalTransactionIdMulti(GTM_TransactionHandle handle[], int txn_count,
                return false;
        }
 
-       *new_txn_count = 0;
        /*
-        * 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.
+        * Now generate a GXID to hadles that do now have a GXID assigned yet.
         */
        for (ii = 0; ii < txn_count; ii++)
        {
-               gtm_txninfo = GTM_HandleToTransactionInfo(handle[ii]);
+               gtm_txninfo = GTM_HandleToTransactionInfo(handles[ii]);
                Assert(gtm_txninfo);
 
                if (GlobalTransactionIdIsValid(gtm_txninfo->gti_gxid))
                {
-                       gxid[ii] = gtm_txninfo->gti_gxid;
+                       gxids[ii] = gtm_txninfo->gti_gxid;
                        elog(DEBUG1, "GTM_TransactionInfo has XID already assgined - %s:%d",
-                                       gtm_txninfo->gti_global_session_id, gxid[ii]);
+                                       gtm_txninfo->gti_global_session_id, gxids[ii]);
                        continue;
                }
 
@@ -501,12 +729,16 @@ GTM_GetGlobalTransactionIdMulti(GTM_TransactionHandle handle[], int txn_count,
 
                elog(DEBUG1, "Assigning new transaction ID = %s:%d",
                                gtm_txninfo->gti_global_session_id, xid);
-               gxid[ii] = gtm_txninfo->gti_gxid = xid;
-               new_handle[*new_txn_count] = gtm_txninfo->gti_handle;
-               *new_txn_count = *new_txn_count + 1;
+
+               gxids[ii] = gtm_txninfo->gti_gxid = xid;
+
+               /* only return the new handles when requested */
+               if (new_handles)
+                       new_handles[new_handles_count++] = gtm_txninfo->gti_handle;
        }
 
-       /* Periodically write the xid and sequence info out to the control file.
+       /*
+        * Periodically write the xid and sequence info out to the control file.
         * Try and handle wrapping, too.
         */
        if (GlobalTransactionIdIsValid(xid) &&
@@ -518,38 +750,49 @@ GTM_GetGlobalTransactionIdMulti(GTM_TransactionHandle handle[], int txn_count,
 
        if (GTM_NeedXidRestoreUpdate())
                GTM_SetNeedBackup();
+
        GTM_RWLockRelease(&GTMTransactions.gt_XidGenLock);
 
        /* save control info when not holding the XidGenLock */
        if (save_control)
                SaveControlInfo();
 
+       if (new_txn_count)
+               *new_txn_count = new_handles_count;
+
+       elog(DEBUG1, "GTM_GetGlobalTransactionIdMulti: assigned %d new GXIDs for %d handles",
+                                new_handles_count, txn_count);
+
        return true;
 }
 
 /*
- * Allocate the next XID for my new transaction
+ * GTM_GetGlobalTransactionId
+ *             Allocate GXID for a new transaction.
  *
- * The new XID is also stored into the transaction info structure of the given
- * transaction before returning.
+ * The new GXID is stored into the transaction info structure of the given
+ * transaction before returning (not just returned).
  */
 GlobalTransactionId
 GTM_GetGlobalTransactionId(GTM_TransactionHandle handle)
 {
-       GlobalTransactionId gxid;
-       GTM_TransactionHandle new_handle;
-       int new_count;
+       GlobalTransactionId gxid = InvalidGlobalTransactionId;
+
+       GTM_GetGlobalTransactionIdMulti(&handle, 1, &gxid, NULL, NULL);
+
+       elog(DEBUG1, "GTM_GetGlobalTransactionId: assigned new GXID %u", gxid);
+
+       Assert(GlobalTransactionIdIsValid(gxid));
 
-       GTM_GetGlobalTransactionIdMulti(&handle, 1, &gxid, &new_handle,
-                       &new_count);
        return gxid;
 }
 
 /*
- * Read nextXid but don't allocate it.
+ * GTM_ReadNewGlobalTransactionId
+ *             Reads nextXid, but do not allocate it (advance to te next one).
  */
 GlobalTransactionId
-ReadNewGlobalTransactionId(void)
+GTM_ReadNewGlobalTransactionId(void)
 {
        GlobalTransactionId xid;
 
@@ -561,35 +804,75 @@ ReadNewGlobalTransactionId(void)
 }
 
 /*
- * Set the nextXid.
+ * GTM_SetNextGlobalTransactionId
+ *             Set the next global XID.
  *
  * The GXID is usually read from a control file and set when the GTM is
- * started. When the GTM is finally shutdown, the next to-be-assigned GXID is
- * stroed in the control file.
+ * started. When the GTM is finally shutdown, the next to-be-assigned GXID
+ * is stored in the control file.
  *
- * XXX We don't yet handle any crash recovery. So if the GTM is no shutdown normally...
+ * The function also switches the GTM from 'starting' to 'running' state.
  *
- * This is handled by gtm_backup.c.  Anyway, because this function is to be called by
- * GTM_RestoreTransactionId() and the backup will be performed afterwords,
- * we don't care the new value of GTMTransactions.gt_nextXid here.
+ * This is handled by gtm_backup.c.  Anyway, because this function is to be
+ * called by GTM_RestoreTransactionId() and the backup will be performed
+ * afterwards, we don't care the new value of GTMTransactions.gt_nextXid here
+ * (it may even be invalid or stale).
+ *
+ * XXX We don't yet handle any crash recovery. So if the GTM did not shutdown
+ * cleanly, it's not quite sure what'll happen.
  */
 void
-SetNextGlobalTransactionId(GlobalTransactionId gxid)
+GTM_SetNextGlobalTransactionId(GlobalTransactionId gxid)
 {
+       /* we should only be calling this during GTM startup */
+       Assert(GTMTransactions.gt_gtm_state == GTM_STARTING);
+
        GTM_RWLockAcquire(&GTMTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE);
        GTMTransactions.gt_nextXid = gxid;
        GTMTransactions.gt_gtm_state = GTM_RUNNING;
        GTM_RWLockRelease(&GTMTransactions.gt_XidGenLock);
+
        return;
 }
 
+/*
+ * GTM_SetControlXid
+ *             Sets the control GXID.
+ */
 void
-SetControlXid(GlobalTransactionId gxid)
+GTM_SetControlXid(GlobalTransactionId gxid)
 {
+       elog(DEBUG1, "GTM_SetControlXid: setting control GXID %u", gxid);
        ControlXid = gxid;
 }
 
-/* Transaction Control */
+/*
+ * GTM_BeginTransactionMulti
+ *             Starts transactions on provided global sessions, if needed.
+ *
+ * If there already is an open transaction on a global session, the existing
+ * transaction handle is reused.
+ *
+ * The transaction handles are initialized in the txns[] array, and the
+ * number of elements is returned (in general it will be equal to txn_count).
+ *
+ * Input:
+ *             isolevel[]                      - requested isolation levels
+ *             readonly[]                      - flags for read-only sessions
+ *             global_sessionid[]      - IDs of global sessions
+ *             connid[]                        - IDs of proxy connections
+ *             txn_count                       - number of sessions/transactions
+ *
+ * Output:
+ *             txns[]                          - initialized transaction handles
+ *
+ * Returns number of transaction handles returned in txns[] array.
+ *
+ * The caller is responsible for ensuring the input/output arrays are
+ * correctly sized (all should have at least txn_count elements).
+ *
+ * XXX The transaction handles are allocated in TopMostMemoryContext.
+ */
 static int
 GTM_BeginTransactionMulti(GTM_IsolationLevel isolevel[],
                                         bool readonly[],
@@ -602,6 +885,9 @@ GTM_BeginTransactionMulti(GTM_IsolationLevel isolevel[],
        MemoryContext oldContext;
        int kk;
 
+       /* make sure we received all the required array paremeters */
+       Assert(isolevel && readonly && global_sessionid && txns && connid);
+
        memset(gtm_txninfo, 0, sizeof (gtm_txninfo));
 
        /*
@@ -622,6 +908,10 @@ GTM_BeginTransactionMulti(GTM_IsolationLevel isolevel[],
                GTM_TransactionHandle txn =
                                GTM_GlobalSessionIDToHandle(global_sessionid[kk]);
 
+               /*
+                * If tere already is a transaction open on the global session, reuse
+                * it and continue with the next one.
+                */
                if (txn != InvalidTransactionHandle)
                {
                        gtm_txninfo[kk] = GTM_HandleToTransactionInfo(txn);
@@ -633,8 +923,25 @@ GTM_BeginTransactionMulti(GTM_IsolationLevel isolevel[],
                }
 
                /*
-                * We had no cached slots. Now find a free slot in the transation array
-                * and store the transaction info structure there
+                * We had no cached slots. Now find a free slot in the transaction array
+                * and store the new transaction info structure there.
+                *
+                * When looking for a new empty slot in the transactions array, we do not
+                * start at index 0 as the transaction are likely squashed there. Instead
+                * we track the ID of the last assigned slot (gt_lastslot), and start from
+                * that index. We do exactly GTM_MAX_GLOBAL_TRANSACTIONS steps, so we may
+                * walk the whole array in the worst case (everything is full).
+                *
+                * The assumptiion is that the "oldest" slots will be eventually freed, so
+                * when we get back to them (after about GTM_MAX_GLOBAL_TRANSACTIONS
+                * transaction), the slots will be free again.
+                *
+                * XXX This will degrade with many open global transactions, as the array
+                * gets "more full". In that case we could perhaps track the free slots
+                * in a freelist (similarly to gt_open_transactions), or something.
+                *
+                * XXX We could also track the number of assigned slots, to quickly detect
+                * when there are no free slots. But that seems unlikely.
                 */
                startslot = GTMTransactions.gt_lastslot + 1;
                if (startslot >= GTM_MAX_GLOBAL_TRANSACTIONS)
@@ -650,19 +957,25 @@ GTM_BeginTransactionMulti(GTM_IsolationLevel isolevel[],
                                break;
                        }
 
+                       /*
+                        * We got back to the starting point, and haven't found any free slot.
+                        * That means we have reached GTM_MAX_GLOBAL_TRANSACTIONS.
+                        */
                        if (ii == GTMTransactions.gt_lastslot)
                        {
                                GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
                                ereport(ERROR,
-                                               (ERANGE, errmsg("Max transaction limit reached")));
+                                               (ERANGE, errmsg("Max global transactions limit reached (%d)",
+                                                                               GTM_MAX_GLOBAL_TRANSACTIONS)));
                        }
                }
 
-               init_GTM_TransactionInfo(gtm_txninfo[kk], ii, isolevel[kk],
+               GTM_TransactionInfo_Init(gtm_txninfo[kk], ii, isolevel[kk],
                                GetMyThreadInfo->thr_client_id, connid[kk],
                                global_sessionid[kk],
                                readonly[kk]);
 
+               /* remember which slot we used for the next loop */
                GTMTransactions.gt_lastslot = ii;
 
                txns[kk] = ii;
@@ -683,7 +996,22 @@ GTM_BeginTransactionMulti(GTM_IsolationLevel isolevel[],
        return txn_count;
 }
 
-/* Transaction Control */
+/*
+ * GTM_BeginTransaction
+ *             Starts transaction on provided global session.
+ *
+ * If there already is an open transaction on the global session, the
+ * existing transaction handle is reused.
+ *
+ * Input:
+ *             isolevel                        - requested isolation level
+ *             readonly                        - should the transaction be read-only
+ *             global_sessionid        - ID of theh global session
+ *
+ * Returns an initialized transaction handle.
+ *
+ * XXX The transaction handle is allocated in TopMostMemoryContext.
+ */
 static GTM_TransactionHandle
 GTM_BeginTransaction(GTM_IsolationLevel isolevel,
                                         bool readonly,
@@ -693,11 +1021,16 @@ GTM_BeginTransaction(GTM_IsolationLevel isolevel,
        GTMProxy_ConnID connid = -1;
 
        GTM_BeginTransactionMulti(&isolevel, &readonly, &global_sessionid, &connid, 1, &txn);
+
        return txn;
 }
 
+/*
+ * GTM_TransactionInfo_Init
+ *             Initialize info about a transaction and store it in global array.
+ */
 static void
-init_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo,
+GTM_TransactionInfo_Init(GTM_TransactionInfo *gtm_txninfo,
                                                 GTM_TransactionHandle txn,
                                                 GTM_IsolationLevel isolevel,
                                                 uint32 client_id,
@@ -750,11 +1083,23 @@ init_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo,
 
 
 /*
- * Clean up the TransactionInfo slot and pfree all the palloc'ed memory,
- * except txid array of the snapshot, which is reused.
+ * GTM_TransactionInfo_Clean
+ *             Mark a transaction slot as empty and release memory.
+ *
+ * Most of the cleanup is about dealing with sequences modified in the
+ * transaction, and what exactly needs to happen depends on whether the
+ * transaction is being committed or aborted.
+ *
+ * XXX We do not pfree the txid array of the snapshot, which may be referenced
+ * by by multiple transactions. But we should never really have more than
+ * GTM_MAX_GLOBAL_TRANSACTIONS of them (with 16k transactions, that's about
+ * 1GB of RAM).
+ *
+ * XXX Do we expect this being called only for transactions that are currently
+ * being aborted/committed, or in other states too (for example "starting")?
  */
 static void
-clean_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo)
+GTM_TransactionInfo_Clean(GTM_TransactionInfo *gtm_txninfo)
 {
        gtm_ListCell *lc;
 
@@ -786,7 +1131,6 @@ clean_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo)
                        GTM_SeqRestoreAltered(gtm_lfirst(lc));
                }
 
-
        }
        else if (gtm_txninfo->gti_state == GTM_TXN_COMMIT_IN_PROGRESS)
        {
@@ -833,14 +1177,21 @@ clean_GTM_TransactionInfo(GTM_TransactionInfo *gtm_txninfo)
        }
 }
 
-
+/*
+ * GTM_BkupBeginTransactionMulti
+ *             Open multiple transactions on provided global sessions.
+ *
+ * XXX I'm not sure why we need this when GTM_BeginTransactionMulti does the
+ * same thing (and it allocates everything in TopMostMemoryContext too).
+ * Maybe that we fail if some of the transactions fail to start?
+ */
 static void
-GTM_BkupBeginTransactionMulti(GTM_IsolationLevel *isolevel,
-                                                         bool *readonly,
-                                                         const char **global_sessionid,
-                                                         uint32 *client_id,
-                                                         GTMProxy_ConnID *connid,
-                                                         int   txn_count)
+GTM_BkupBeginTransactionMulti(GTM_IsolationLevel isolevel[],
+                                                         bool readonly[],
+                                                         const char *global_sessionid[],
+                                                         uint32 client_id[],
+                                                         GTMProxy_ConnID connid[],
+                                                         int txn_count)
 {
        GTM_TransactionHandle txn[GTM_MAX_GLOBAL_TRANSACTIONS];
        MemoryContext oldContext;
@@ -859,6 +1210,13 @@ GTM_BkupBeginTransactionMulti(GTM_IsolationLevel *isolevel,
        MemoryContextSwitchTo(oldContext);
 }
 
+/*
+ * GTM_BkupBeginTransaction
+ *             Starts transaction on provided global session.
+ *
+ * XXX I'm not sure why we need this when GTM_BeginTransaction does the
+ * same thing (and it allocates everything in TopMostMemoryContext too).
+ */
 static void
 GTM_BkupBeginTransaction(GTM_IsolationLevel isolevel,
                                                 bool readonly,
@@ -873,7 +1231,19 @@ GTM_BkupBeginTransaction(GTM_IsolationLevel isolevel,
 }
 
 /*
- * Rollback multiple transactions in one go
+ * GTM_RollbackTransactionMulti
+ *             Rollback multiple global transactions (handles) in one go.
+ *
+ * The function expects txn_count handles to be supplied in the txn[] array.
+ * We first mark all transactions as GTM_TXN_ABORT_IN_PROGRESS and then
+ * remove them.
+ *
+ * Rollback status for each of supplied transaction handle is returned in the
+ * status[] array (so it has to have space for at least txn_count elements).
+ * If a handle is not provided (it's NULL in txn[]), the matching status will
+ * be set to STATUS_ERROR.
+ *
+ * The function returns txn_count, that is the number of supplied handles.
  */
 static int
 GTM_RollbackTransactionMulti(GTM_TransactionHandle txn[], int txn_count, int status[])
@@ -881,6 +1251,10 @@ GTM_RollbackTransactionMulti(GTM_TransactionHandle txn[], int txn_count, int sta
        GTM_TransactionInfo *gtm_txninfo[txn_count];
        int ii;
 
+       ereport(DEBUG1,
+               (ERANGE, errmsg("GTM_RollbackTransactionMulti: rollbing back %d transactions",
+                                               txn_count)));
+
        for (ii = 0; ii < txn_count; ii++)
        {
                gtm_txninfo[ii] = GTM_HandleToTransactionInfo(txn[ii]);
@@ -892,11 +1266,13 @@ GTM_RollbackTransactionMulti(GTM_TransactionHandle txn[], int txn_count, int sta
                }
 
                /*
-                * Mark the transaction as being aborted
+                * Mark the transaction as being aborted. We need to acquire the lock
+                * on that transaction to do that.
                 */
                GTM_RWLockAcquire(&gtm_txninfo[ii]->gti_lock, GTM_LOCKMODE_WRITE);
                gtm_txninfo[ii]->gti_state = GTM_TXN_ABORT_IN_PROGRESS;
                GTM_RWLockRelease(&gtm_txninfo[ii]->gti_lock);
+
                status[ii] = STATUS_OK;
        }
 
@@ -906,7 +1282,8 @@ GTM_RollbackTransactionMulti(GTM_TransactionHandle txn[], int txn_count, int sta
 }
 
 /*
- * Rollback a transaction
+ * GTM_RollbackTransaction
+ *             Rollback a single global transaction, identified by a handle.
  */
 static int
 GTM_RollbackTransaction(GTM_TransactionHandle txn)
@@ -917,11 +1294,45 @@ GTM_RollbackTransaction(GTM_TransactionHandle txn)
 }
 
 /*
- * Commit multiple transactions in one go
+ * GTM_CommitTransactionMulti
+ *             Commit multiple global transactions in one go.
+ *
+ * Commits txn_count transactions identified by handles passed in txn[] array,
+ * and returns the status for each of them in status[] array.
+ *
+ * It is also possible to provide an array of transactions that have to finish
+ * before txn[] transactions can be committed. If some of the transactions in
+ * waited_xids[] (with waited_xid_count elements) are still in progress, the
+ * transactions will not be committed and will be marked as delayed.
+ *
+ * Input:
+ *             txn[]                           - array of transaction handles to commit
+ *             txn_count                       - number of transaction handles in txn[]
+ *             waited_xid_count        - number of GIXDs in waited_xids[]
+ *             waited_xids[]           - GIXDs to wait for before the commit
+ *
+ * Output:
+ *             status[]                        - outcome of the commit for each txn[] handle
+ *
+ * The function returns the number of successfully committed transactions
+ * (and removed from the global array).
+ *
+ * The status[] array contains the commit status for each txn[] element, i.e.
+ * txn_count elements. There are three possible values:
+ *
+ *  - STATUS_OK                        transaction was committed (and removed)
+ *     - STATUS_DELAYED        commit is delayed due to in-progress transactions
+ *     - STATUS_ERROR          invalid (NULL) transaction handle
+ *
+ * XXX Do we need to repeat the loop over waited_xids for every transaction?
+ * Maybe we could check it once at the beginning. The only case why that might
+ * fail is probably when waited_xids[] and txn[] overlap, some of the GXIDs
+ * we're waiting for are also on the list of transactions to commit. But maybe
+ * that's not allowed, as such transaction would get delayed by itself.
  */
 static int
 GTM_CommitTransactionMulti(GTM_TransactionHandle txn[], int txn_count,
-               int waited_xid_count, GlobalTransactionId *waited_xids,
+               int waited_xid_count, GlobalTransactionId waited_xids[],
                int status[])
 {
        GTM_TransactionInfo *gtm_txninfo[txn_count];
@@ -936,15 +1347,19 @@ GTM_CommitTransactionMulti(GTM_TransactionHandle txn[], int txn_count,
 
                gtm_txninfo[ii] = GTM_HandleToTransactionInfo(txn[ii]);
 
+               /* We should not be committing handles that are not initialized. */
                if (gtm_txninfo[ii] == NULL)
                {
+                       elog(WARNING, "GTM_CommitTransactionMulti: can not commit non-initialized handle");
                        status[ii] = STATUS_ERROR;
                        continue;
                }
 
                /*
-                * If any of the waited_xids is still running, we must delay commit for
-                * this transaction till all such waited_xids are finished
+                * See if the current transaction depends on other transactions that are
+                * still running (possiby one of those we're currently committing?). In
+                * that case we have to delay commit of this transaction until after those
+                * transaction finish.
                 */
                for (jj = 0; jj < waited_xid_count; jj++)
                {
@@ -957,8 +1372,12 @@ GTM_CommitTransactionMulti(GTM_TransactionHandle txn[], int txn_count,
                        }
                }
 
+               /* We're waiting for in-progress transactions, so let's delay the commit. */
                if (waited_xid_running) 
                {
+                       elog(WARNING, "GTM_CommitTransactionMulti: delaying commit of handle %d",
+                                                 gtm_txninfo[ii]->gti_gxid);
+
                        status[ii] = STATUS_DELAYED;
                        continue;
                }
@@ -969,53 +1388,86 @@ GTM_CommitTransactionMulti(GTM_TransactionHandle txn[], int txn_count,
                GTM_RWLockAcquire(&gtm_txninfo[ii]->gti_lock, GTM_LOCKMODE_WRITE);
                gtm_txninfo[ii]->gti_state = GTM_TXN_COMMIT_IN_PROGRESS;
                GTM_RWLockRelease(&gtm_txninfo[ii]->gti_lock);
+
                status[ii] = STATUS_OK;
 
+               /* Keep track of transactions to remove from global array. */
                remove_txninfo[remove_count++] = gtm_txninfo[ii];
        }
 
+       /*
+        * Remove the transaction from the global array, but only those that we
+        * managed to switch to GTM_TXN_COMMIT_IN_PROGRESS state.
+        */
        GTM_RemoveTransInfoMulti(remove_txninfo, remove_count);
 
        return remove_count;
 }
 
 /*
- * Prepare a transaction
+ * GTM_CommitTransaction
+ *             Commit a single global transaction handle.
+ *
+ * Similaarly to GTM_CommitTransactionMulti, it's possible to specify an array
+ * of GIXDs that should have completed before the transaction gets committed.
+ *
+ * Returns STATUS_OK (committed), STATUS_DELAYED (waiting by in-progress
+ * transactions) or STATUS_ERROR (txninfo for the handle not found).
+ */
+static int
+GTM_CommitTransaction(GTM_TransactionHandle txn, int waited_xid_count,
+               GlobalTransactionId *waited_xids)
+{
+       int status;
+       GTM_CommitTransactionMulti(&txn, 1, waited_xid_count, waited_xids, &status);
+       return status;
+}
+
+/*
+ * GTM_PrepareTransaction
+ *             Prepare transaction for commit (in 2PC protocol).
+ *
+ * Prepare a transaction for commit, and returns STATUS_OK or STATUS_ERROR.
+ *
+ * XXX This should probably check the initial gti_state (at least by assert).
+ * I assume we can only see transactions in GTM_TXN_PREPARE_IN_PROGRESS.
  */
 static int
 GTM_PrepareTransaction(GTM_TransactionHandle txn)
 {
+       int     state;
        GTM_TransactionInfo *gtm_txninfo = NULL;
 
        gtm_txninfo = GTM_HandleToTransactionInfo(txn);
 
        if (gtm_txninfo == NULL)
+       {
+               elog(WARNING, "GTM_PrepareTransaction: can't prepare transaction handle %d (txninfo is NULL)",
+                        txn);
                return STATUS_ERROR;
+       }
 
        /*
         * Mark the transaction as prepared
         */
        GTM_RWLockAcquire(&gtm_txninfo->gti_lock, GTM_LOCKMODE_WRITE);
+       state = gtm_txninfo->gti_state;
        gtm_txninfo->gti_state = GTM_TXN_PREPARED;
        GTM_RWLockRelease(&gtm_txninfo->gti_lock);
 
-       return STATUS_OK;
-}
+       /* The initial state should have been PREPARE_IN_PROGRESS. */
+       Assert(state == GTM_TXN_PREPARE_IN_PROGRESS);
 
-/*
- * Commit a transaction
- */
-static int
-GTM_CommitTransaction(GTM_TransactionHandle txn, int waited_xid_count,
-               GlobalTransactionId *waited_xids)
-{
-       int status;
-       GTM_CommitTransactionMulti(&txn, 1, waited_xid_count, waited_xids, &status);
-       return status;
+       return STATUS_OK;
 }
 
 /*
- * Prepare a transaction
+ * GTM_StartPreparedTransaction
+ *             Start preparing a transaction (set GTM_TXN_PREPARE_IN_PROGRESS).
+ *
+ * Returns either STATUS_OK when the transaction was succesfully switched to
+ * GTM_TXN_PREPARE_IN_PROGRESS, or STATUS_ERROR when the state change fails
+ * for some reason (unknown transaction handle, duplicate GID).
  */
 static int
 GTM_StartPreparedTransaction(GTM_TransactionHandle txn,
@@ -1025,19 +1477,19 @@ GTM_StartPreparedTransaction(GTM_TransactionHandle txn,
        GTM_TransactionInfo *gtm_txninfo = GTM_HandleToTransactionInfo(txn);
 
        if (gtm_txninfo == NULL)
+       {
+               elog(WARNING, "GTM_StartPreparedTransaction: unknown handle %d", txn);
                return STATUS_ERROR;
+       }
 
        /*
         * Check if given GID is already in use by another transaction.
         */
        if (GTM_GIDToHandle(gid) != InvalidTransactionHandle)
+       {
+               elog(WARNING, "GTM_StartPreparedTransaction: GID %s already exists", gid);
                return STATUS_ERROR;
-
-       /*
-        * Check if given GID is already in use by another transaction.
-        */
-       if (GTM_GIDToHandle(gid) != InvalidTransactionHandle)
-               return STATUS_ERROR;
+       }
 
        /*
         * Mark the transaction as being prepared
@@ -1060,6 +1512,16 @@ GTM_StartPreparedTransaction(GTM_TransactionHandle txn,
        return STATUS_OK;
 }
 
+/*
+ * GTM_GetGIDData
+ *             Returns gti_gxid and nodestring for a transaction handle.
+ *
+ * The nodestring (if available) is allocated in TopMostMemoryContext.
+ * If there is no matching transaction info (no open transaction for the
+ * handle), the rertur value is STATUS_ERROR.
+ *
+ * In case of success the return value is STATUS_OK.
+ */
 static int
 GTM_GetGIDData(GTM_TransactionHandle prepared_txn,
                           GlobalTransactionId *prepared_gxid,
@@ -1068,6 +1530,8 @@ GTM_GetGIDData(GTM_TransactionHandle prepared_txn,
        GTM_TransactionInfo     *gtm_txninfo = NULL;
        MemoryContext           oldContext;
 
+       Assert(prepared_gxid);
+
        oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
 
        gtm_txninfo = GTM_HandleToTransactionInfo(prepared_txn);
@@ -1090,6 +1554,103 @@ GTM_GetGIDData(GTM_TransactionHandle prepared_txn,
        return STATUS_OK;
 }
 
+/*
+ * GTM_BkupBeginTransactionGetGXIDMulti
+ *
+ * XXX Not sure what this does.
+ */
+static void
+GTM_BkupBeginTransactionGetGXIDMulti(GlobalTransactionId *gxid,
+                                                                        GTM_IsolationLevel *isolevel,
+                                                                        bool *readonly,
+                                                                        const char **global_sessionid,
+                                                                        uint32 *client_id,
+                                                                        GTMProxy_ConnID *connid,
+                                                                        int txn_count)
+{
+       GTM_TransactionHandle txn[GTM_MAX_GLOBAL_TRANSACTIONS];
+       GTM_TransactionInfo *gtm_txninfo;
+       int ii;
+       int count;
+       MemoryContext oldContext;
+
+       bool save_control = false;
+       GlobalTransactionId xid = InvalidGlobalTransactionId;
+
+       oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
+
+       count = GTM_BeginTransactionMulti(isolevel, readonly, global_sessionid,
+                                                                         connid, txn_count, txn);
+       if (count != txn_count)
+               ereport(ERROR,
+                               (EINVAL,
+                                errmsg("Failed to start %d new transactions", txn_count)));
+
+       elog(DEBUG2, "GTM_BkupBeginTransactionGetGXIDMulti - count %d", count);
+
+       //XCPTODO check oldContext = MemoryContextSwitchTo(TopMemoryContext);
+       GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
+
+       for (ii = 0; ii < txn_count; ii++)
+       {
+               gtm_txninfo = GTM_HandleToTransactionInfo(txn[ii]);
+               gtm_txninfo->gti_gxid = gxid[ii];
+               if (global_sessionid[ii])
+                       strncpy(gtm_txninfo->gti_global_session_id, global_sessionid[ii],
+                                       GTM_MAX_SESSION_ID_LEN);
+
+               elog(DEBUG2, "GTM_BkupBeginTransactionGetGXIDMulti: xid(%u), handle(%u)",
+                               gxid[ii], txn[ii]);
+
+               /*
+                * Advance next gxid -- because this is called at slave only, we don't care the restoration point
+                * here.  Restoration point will be created at promotion.
+                */
+               if (GlobalTransactionIdPrecedesOrEquals(GTMTransactions.gt_nextXid, gxid[ii]))
+                       GTMTransactions.gt_nextXid = gxid[ii] + 1;
+               if (!GlobalTransactionIdIsValid(GTMTransactions.gt_nextXid))    /* Handle wrap around too */
+                       GTMTransactions.gt_nextXid = FirstNormalGlobalTransactionId;
+               xid = GTMTransactions.gt_nextXid;
+       }
+
+       /*
+        * Periodically write the xid and sequence info out to the control file.
+        * Try and handle wrapping, too.
+        */
+       if (GlobalTransactionIdIsValid(xid) &&
+                       (xid - ControlXid > CONTROL_INTERVAL || xid < ControlXid))
+       {
+               save_control = true;
+               ControlXid = xid;
+       }
+
+       GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
+
+       /* save control info when not holding the XidGenLock */
+       if (save_control)
+               SaveControlInfo();
+
+       MemoryContextSwitchTo(oldContext);
+}
+
+/*
+ * GTM_BkupBeginTransactionGetGXID
+ *
+ * XXX Not sure what this does.
+ */
+static void
+GTM_BkupBeginTransactionGetGXID(GlobalTransactionId gxid,
+                                                               GTM_IsolationLevel isolevel,
+                                                               bool readonly,
+                                                               const char *global_sessionid,
+                                                               uint32 client_id)
+{
+       GTMProxy_ConnID connid = -1;
+
+       GTM_BkupBeginTransactionGetGXIDMulti(&gxid, &isolevel,
+                       &readonly, &global_sessionid, &client_id, &connid, 1);
+}
+
 /*
  * Process MSG_TXN_BEGIN message
  */
@@ -1287,92 +1848,6 @@ retry:
        return;
 }
 
-static void
-GTM_BkupBeginTransactionGetGXIDMulti(GlobalTransactionId *gxid,
-                                                                        GTM_IsolationLevel *isolevel,
-                                                                        bool *readonly,
-                                                                        const char **global_sessionid,
-                                                                        uint32 *client_id,
-                                                                        GTMProxy_ConnID *connid,
-                                                                        int txn_count)
-{
-       GTM_TransactionHandle txn[GTM_MAX_GLOBAL_TRANSACTIONS];
-       GTM_TransactionInfo *gtm_txninfo;
-       int ii;
-       int count;
-       MemoryContext oldContext;
-
-       bool save_control = false;
-       GlobalTransactionId xid = InvalidGlobalTransactionId;
-
-       oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
-
-       count = GTM_BeginTransactionMulti(isolevel, readonly, global_sessionid,
-                                                                         connid, txn_count, txn);
-       if (count != txn_count)
-               ereport(ERROR,
-                               (EINVAL,
-                                errmsg("Failed to start %d new transactions", txn_count)));
-
-       elog(DEBUG2, "GTM_BkupBeginTransactionGetGXIDMulti - count %d", count);
-
-       //XCPTODO check oldContext = MemoryContextSwitchTo(TopMemoryContext);
-       GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
-
-       for (ii = 0; ii < txn_count; ii++)
-       {
-               gtm_txninfo = GTM_HandleToTransactionInfo(txn[ii]);
-               gtm_txninfo->gti_gxid = gxid[ii];
-               if (global_sessionid[ii])
-                       strncpy(gtm_txninfo->gti_global_session_id, global_sessionid[ii],
-                                       GTM_MAX_SESSION_ID_LEN);
-
-               elog(DEBUG2, "GTM_BkupBeginTransactionGetGXIDMulti: xid(%u), handle(%u)",
-                               gxid[ii], txn[ii]);
-
-               /*
-                * Advance next gxid -- because this is called at slave only, we don't care the restoration point
-                * here.  Restoration point will be created at promotion.
-                */
-               if (GlobalTransactionIdPrecedesOrEquals(GTMTransactions.gt_nextXid, gxid[ii]))
-                       GTMTransactions.gt_nextXid = gxid[ii] + 1;
-               if (!GlobalTransactionIdIsValid(GTMTransactions.gt_nextXid))    /* Handle wrap around too */
-                       GTMTransactions.gt_nextXid = FirstNormalGlobalTransactionId;
-               xid = GTMTransactions.gt_nextXid;
-       }
-
-       /* Periodically write the xid and sequence info out to the control file.
-        * Try and handle wrapping, too.
-        */
-       if (GlobalTransactionIdIsValid(xid) &&
-                       (xid - ControlXid > CONTROL_INTERVAL || xid < ControlXid))
-       {
-               save_control = true;
-               ControlXid = xid;
-       }
-
-       GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
-
-       /* save control info when not holding the XidGenLock */
-       if (save_control)
-               SaveControlInfo();
-
-       MemoryContextSwitchTo(oldContext);
-}
-
-static void
-GTM_BkupBeginTransactionGetGXID(GlobalTransactionId gxid,
-                                                               GTM_IsolationLevel isolevel,
-                                                               bool readonly,
-                                                               const char *global_sessionid,
-                                                               uint32 client_id)
-{
-       GTMProxy_ConnID connid = -1;
-
-       GTM_BkupBeginTransactionGetGXIDMulti(&gxid, &isolevel,
-                       &readonly, &global_sessionid, &client_id, &connid, 1);
-}
-
 /*
  * Process MSG_BKUP_TXN_BEGIN_GETGXID message
  */
@@ -2690,8 +3165,28 @@ bool GTM_NeedXidRestoreUpdate(void)
        return(GlobalTransactionIdPrecedesOrEquals(GTMTransactions.gt_backedUpXid, GTMTransactions.gt_nextXid));
 }
 
+/*
+ * GTM_RememberCreatedSequence
+ *             Remember a sequence created by a given transaction (GXID).
+ *
+ * When creating a sequence in a transaction, we need to remember it so thah
+ * we can deal with it in case of commit/abort, or when it's later dropped in
+ * the same transaction.
+ *
+ * - If the transaction aborts, we simply remove it from the global structure
+ * (see GTM_SeqRemoveCreated).
+ *
+ * - If the sequence gets dropped in the same transaction (GXID), we can just
+ * remove it from the global structure and also stop tracking it in the
+ * transaction-specific list (see GTM_ForgetCreatedSequence).
+ *
+ * - If the transaction commits, just forget about this tracked sequence.
+ *
+ * See GTM_TransactionInfo_Clean for what happens with the tracked sequences
+ * in case of commit/abort of the global transaction.
+ */
 void
-GTM_ForgetCreatedSequence(GlobalTransactionId gxid, void *seq)
+GTM_RememberCreatedSequence(GlobalTransactionId gxid, void *seq)
 {
        GTM_TransactionInfo *gtm_txninfo;
        GTM_TransactionHandle txn = GTM_GXIDToHandle(gxid);
@@ -2701,20 +3196,15 @@ GTM_ForgetCreatedSequence(GlobalTransactionId gxid, void *seq)
 
        gtm_txninfo = GTM_HandleToTransactionInfo(txn);
        gtm_txninfo->gti_created_seqs =
-               gtm_list_delete(gtm_txninfo->gti_created_seqs, seq);
+               gtm_lappend(gtm_txninfo->gti_created_seqs, seq);
 }
 
 /*
- * Remember sequence created by transaction 'gxid'.
- *
- * This should be removed from the global data structure if the transaction
- * aborts (see GTM_SeqRemoveCreated). If the sequence is later dropped in the
- * same transaction, we remove it from the global structure as well as forget
- * tracking (see GTM_ForgetCreatedSequence). If the transaction commits, just
- * forget about this tracked sequence.
+ * GTM_ForgetCreatedSequence
+ *             Stop tracking a sequence created in a given transaction (GXID).
  */
 void
-GTM_RememberCreatedSequence(GlobalTransactionId gxid, void *seq)
+GTM_ForgetCreatedSequence(GlobalTransactionId gxid, void *seq)
 {
        GTM_TransactionInfo *gtm_txninfo;
        GTM_TransactionHandle txn = GTM_GXIDToHandle(gxid);
@@ -2724,9 +3214,19 @@ GTM_RememberCreatedSequence(GlobalTransactionId gxid, void *seq)
 
        gtm_txninfo = GTM_HandleToTransactionInfo(txn);
        gtm_txninfo->gti_created_seqs =
-               gtm_lappend(gtm_txninfo->gti_created_seqs, seq);
+               gtm_list_delete(gtm_txninfo->gti_created_seqs, seq);
 }
 
+/*
+ * GTM_RememberDroppedSequence
+ *             Remember that transaction GXID modified a given sequence.
+ *
+ * We need to track this, so that we can properly respond to commit/abort of
+ * the global transaction (and either undo or alter the sequence).
+ *
+ * See GTM_TransactionInfo_Clean for what happens with the tracked sequences
+ * in case of commit/abort of the global transaction.
+ */
 void
 GTM_RememberDroppedSequence(GlobalTransactionId gxid, void *seq)
 {
@@ -2741,6 +3241,17 @@ GTM_RememberDroppedSequence(GlobalTransactionId gxid, void *seq)
                gtm_lappend(gtm_txninfo->gti_dropped_seqs, seq);
 }
 
+/*
+ * GTM_RememberDroppedSequence
+ *             Remember that transaction GXID dropped a given sequence.
+ *
+ * We need to track this, so that we can properly respond to commit/abort of
+ * the global transaction (and either reinstate or definitely remove the
+ * sequence).
+ *
+ * See GTM_TransactionInfo_Clean for what happens with the tracked sequences
+ * in case of commit/abort of the global transaction.
+ */
 void
 GTM_RememberAlteredSequence(GlobalTransactionId gxid, void *seq)
 {
index a518db392e1691924f53ec216e1e09e04e6e45ec..f9eeb1d788b260502964db5121ef0e57c8c15b94 100644 (file)
@@ -2367,7 +2367,7 @@ GTM_RestoreTxnInfo(FILE *ctlf, GlobalTransactionId next_gxid,
                         * Add in extra amount in case we had not gracefully stopped
                         */
                        next_gxid = saved_gxid + CONTROL_INTERVAL;
-                       SetControlXid(next_gxid);
+                       GTM_SetControlXid(next_gxid);
                }
                else
                        saved_gxid = next_gxid = InitialGXIDValue_Default;
@@ -2391,7 +2391,7 @@ GTM_RestoreTxnInfo(FILE *ctlf, GlobalTransactionId next_gxid,
                GTMTransactions.gt_recent_global_xmin = next_gxid;
        }
 
-       SetNextGlobalTransactionId(next_gxid);
+       GTM_SetNextGlobalTransactionId(next_gxid);
        elog(LOG, "Restoring last GXID to %u\n", next_gxid);
        elog(LOG, "Restoring global xmin to %u\n",
                        GTMTransactions.gt_recent_global_xmin);
@@ -2413,7 +2413,7 @@ GTM_SaveTxnInfo(FILE *ctlf)
        GlobalTransactionId next_gxid;
        GlobalTransactionId global_xmin = GTMTransactions.gt_recent_global_xmin;
 
-       next_gxid = ReadNewGlobalTransactionId();
+       next_gxid = GTM_ReadNewGlobalTransactionId();
 
        elog(DEBUG1, "Saving transaction info - next_gxid: %u, global_xmin: %u",
                        next_gxid, global_xmin);
index c8b1fdc85504c597ec73e4b196c385cde48cc343..ee3d0a1f0b072e1a17e24a3cd50b0aafb23b208f 100644 (file)
@@ -32,9 +32,9 @@ struct GTM_RestoreContext;
 
 /* gtm/main/gtm_txn.c */
 extern GlobalTransactionId GTM_GetGlobalTransactionId(GTM_TransactionHandle handle);
-extern GlobalTransactionId ReadNewGlobalTransactionId(void);
-extern void SetNextGlobalTransactionId(GlobalTransactionId gxid);
-extern void SetControlXid(GlobalTransactionId gxid);
+extern GlobalTransactionId GTM_ReadNewGlobalTransactionId(void);
+extern void GTM_SetNextGlobalTransactionId(GlobalTransactionId gxid);
+extern void GTM_SetControlXid(GlobalTransactionId gxid);
 extern void GTM_SetShuttingDown(void);
 
 /* for restoration point backup (gtm/main/gtm_backup.c) */
@@ -153,7 +153,7 @@ GTM_TransactionHandle GTM_GXIDToHandle(GlobalTransactionId gxid);
 /* Transaction Control */
 void GTM_InitTxnManager(void);
 void GTM_RemoveAllTransInfos(uint32 client_id, int backend_id);
-uint32 GTMGetLastClientIdentifier(void);
+uint32 GTM_GetLastClientIdentifier(void);
 
 /* processing of messages in gtm_txn.c */
 void ProcessBeginTransactionCommand(Port *myport, StringInfo message);