5757#include "replication/walsender.h"
5858#include "replication/syncrep.h"
5959#include "storage/fd.h"
60+ #include "storage/ipc.h"
6061#include "storage/predicate.h"
6162#include "storage/procarray.h"
6263#include "storage/sinvaladt.h"
@@ -80,25 +81,25 @@ intmax_prepared_xacts = 0;
8081 *
8182 * The lifecycle of a global transaction is:
8283 *
83- * 1. After checking that the requested GID is not in use, set up an
84- *entry in the TwoPhaseState->prepXacts array with the correctXID andGID ,
85- *with locking_xid = my own XID and valid = false .
84+ * 1. After checking that the requested GID is not in use, set up an entry in
85+ * the TwoPhaseState->prepXacts array with the correctGID andvalid = false ,
86+ *and mark it as locked by my backend .
8687 *
8788 * 2. After successfully completing prepare, set valid = true and enter the
8889 * referenced PGPROC into the global ProcArray.
8990 *
90- * 3. To begin COMMIT PREPARED or ROLLBACK PREPARED, check that the entry
91- *is valid andits locking_xid is no longer active, then store my current
92- *XID intolocking_xid . This prevents concurrent attempts to commit or
93- * rollback the same prepared xact.
91+ * 3. To begin COMMIT PREPARED or ROLLBACK PREPARED, check that the entry is
92+ * valid andnot locked, then mark the entry as locked by storing my current
93+ *backend ID intolocking_backend . This prevents concurrent attempts to
94+ *commit or rollback the same prepared xact.
9495 *
9596 * 4. On completion of COMMIT PREPARED or ROLLBACK PREPARED, remove the entry
9697 * from the ProcArray and the TwoPhaseState->prepXacts array and return it to
9798 * the freelist.
9899 *
99100 * Note that if the preparing transaction fails between steps 1 and 2, the
100- * entrywill remain in prepXacts until recycled. We can detect recyclable
101- *entries by checking for valid = false and locking_xid no longer active .
101+ * entrymust be removed so that the GID and the GlobalTransaction struct
102+ *can be reused. See AtAbort_Twophase() .
102103 *
103104 * typedef struct GlobalTransactionData *GlobalTransaction appears in
104105 * twophase.h
@@ -113,8 +114,8 @@ typedef struct GlobalTransactionData
113114TimestampTz prepared_at ;/* time of preparation */
114115XLogRecPtr prepare_lsn ;/* XLOG offset of prepare record */
115116Oid owner ;/* ID of user that executed the xact */
116- TransactionId locking_xid ; /*top-level XID of backend working on xact */
117- bool valid ;/* TRUE iffully prepared */
117+ BackendId locking_backend ; /* backendcurrently working on the xact */
118+ bool valid ;/* TRUE ifPGPROC entry is in proc array */
118119char gid [GIDSIZE ];/* The GID assigned to the prepared xact */
119120}GlobalTransactionData ;
120121
@@ -139,6 +140,12 @@ typedef struct TwoPhaseStateData
139140
140141static TwoPhaseStateData * TwoPhaseState ;
141142
143+ /*
144+ * Global transaction entry currently locked by us, if any.
145+ */
146+ static GlobalTransaction MyLockedGxact = NULL ;
147+
148+ static bool twophaseExitRegistered = false;
142149
143150static void RecordTransactionCommitPrepared (TransactionId xid ,
144151int nchildren ,
@@ -155,6 +162,7 @@ static void RecordTransactionAbortPrepared(TransactionId xid,
155162RelFileNode * rels );
156163static void ProcessRecords (char * bufptr ,TransactionId xid ,
157164const TwoPhaseCallback callbacks []);
165+ static void RemoveGXact (GlobalTransaction gxact );
158166
159167
160168/*
@@ -228,6 +236,74 @@ TwoPhaseShmemInit(void)
228236Assert (found );
229237}
230238
239+ /*
240+ * Exit hook to unlock the global transaction entry we're working on.
241+ */
242+ static void
243+ AtProcExit_Twophase (int code ,Datum arg )
244+ {
245+ /* same logic as abort */
246+ AtAbort_Twophase ();
247+ }
248+
249+ /*
250+ * Abort hook to unlock the global transaction entry we're working on.
251+ */
252+ void
253+ AtAbort_Twophase (void )
254+ {
255+ if (MyLockedGxact == NULL )
256+ return ;
257+
258+ /*
259+ * What to do with the locked global transaction entry? If we were in
260+ * the process of preparing the transaction, but haven't written the WAL
261+ * record and state file yet, the transaction must not be considered as
262+ * prepared. Likewise, if we are in the process of finishing an
263+ * already-prepared transaction, and fail after having already written
264+ * the 2nd phase commit or rollback record to the WAL, the transaction
265+ * should not be considered as prepared anymore. In those cases, just
266+ * remove the entry from shared memory.
267+ *
268+ * Otherwise, the entry must be left in place so that the transaction
269+ * can be finished later, so just unlock it.
270+ *
271+ * If we abort during prepare, after having written the WAL record, we
272+ * might not have transfered all locks and other state to the prepared
273+ * transaction yet. Likewise, if we abort during commit or rollback,
274+ * after having written the WAL record, we might not have released
275+ * all the resources held by the transaction yet. In those cases, the
276+ * in-memory state can be wrong, but it's too late to back out.
277+ */
278+ if (!MyLockedGxact -> valid )
279+ {
280+ RemoveGXact (MyLockedGxact );
281+ }
282+ else
283+ {
284+ LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
285+
286+ MyLockedGxact -> locking_backend = InvalidBackendId ;
287+
288+ LWLockRelease (TwoPhaseStateLock );
289+ }
290+ MyLockedGxact = NULL ;
291+ }
292+
293+ /*
294+ * This is called after we have finished transfering state to the prepared
295+ * PGXACT entry.
296+ */
297+ void
298+ PostPrepare_Twophase ()
299+ {
300+ LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
301+ MyLockedGxact -> locking_backend = InvalidBackendId ;
302+ LWLockRelease (TwoPhaseStateLock );
303+
304+ MyLockedGxact = NULL ;
305+ }
306+
231307
232308/*
233309 * MarkAsPreparing
@@ -259,29 +335,15 @@ MarkAsPreparing(TransactionId xid, const char *gid,
259335errmsg ("prepared transactions are disabled" ),
260336errhint ("Set max_prepared_transactions to a nonzero value." )));
261337
262- LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
263-
264- /*
265- * First, find and recycle any gxacts that failed during prepare. We do
266- * this partly to ensure we don't mistakenly say their GIDs are still
267- * reserved, and partly so we don't fail on out-of-slots unnecessarily.
268- */
269- for (i = 0 ;i < TwoPhaseState -> numPrepXacts ;i ++ )
338+ /* on first call, register the exit hook */
339+ if (!twophaseExitRegistered )
270340{
271- gxact = TwoPhaseState -> prepXacts [i ];
272- if (!gxact -> valid && !TransactionIdIsActive (gxact -> locking_xid ))
273- {
274- /* It's dead Jim ... remove from the active array */
275- TwoPhaseState -> numPrepXacts -- ;
276- TwoPhaseState -> prepXacts [i ]= TwoPhaseState -> prepXacts [TwoPhaseState -> numPrepXacts ];
277- /* and put it back in the freelist */
278- gxact -> next = TwoPhaseState -> freeGXacts ;
279- TwoPhaseState -> freeGXacts = gxact ;
280- /* Back up index count too, so we don't miss scanning one */
281- i -- ;
282- }
341+ on_shmem_exit (AtProcExit_Twophase ,0 );
342+ twophaseExitRegistered = true;
283343}
284344
345+ LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
346+
285347/* Check for conflicting GID */
286348for (i = 0 ;i < TwoPhaseState -> numPrepXacts ;i ++ )
287349{
@@ -339,14 +401,20 @@ MarkAsPreparing(TransactionId xid, const char *gid,
339401gxact -> prepare_lsn .xlogid = 0 ;
340402gxact -> prepare_lsn .xrecoff = 0 ;
341403gxact -> owner = owner ;
342- gxact -> locking_xid = xid ;
404+ gxact -> locking_backend = MyBackendId ;
343405gxact -> valid = false;
344406strcpy (gxact -> gid ,gid );
345407
346408/* And insert it into the active array */
347409Assert (TwoPhaseState -> numPrepXacts < max_prepared_xacts );
348410TwoPhaseState -> prepXacts [TwoPhaseState -> numPrepXacts ++ ]= gxact ;
349411
412+ /*
413+ * Remember that we have this GlobalTransaction entry locked for us.
414+ * If we abort after this, we must release it.
415+ */
416+ MyLockedGxact = gxact ;
417+
350418LWLockRelease (TwoPhaseStateLock );
351419
352420return gxact ;
@@ -409,6 +477,13 @@ LockGXact(const char *gid, Oid user)
409477{
410478int i ;
411479
480+ /* on first call, register the exit hook */
481+ if (!twophaseExitRegistered )
482+ {
483+ on_shmem_exit (AtProcExit_Twophase ,0 );
484+ twophaseExitRegistered = true;
485+ }
486+
412487LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
413488
414489for (i = 0 ;i < TwoPhaseState -> numPrepXacts ;i ++ )
@@ -423,15 +498,11 @@ LockGXact(const char *gid, Oid user)
423498continue ;
424499
425500/* Found it, but has someone else got it locked? */
426- if (TransactionIdIsValid (gxact -> locking_xid ))
427- {
428- if (TransactionIdIsActive (gxact -> locking_xid ))
429- ereport (ERROR ,
430- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
431- errmsg ("prepared transaction with identifier \"%s\" is busy" ,
432- gid )));
433- gxact -> locking_xid = InvalidTransactionId ;
434- }
501+ if (gxact -> locking_backend != InvalidBackendId )
502+ ereport (ERROR ,
503+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
504+ errmsg ("prepared transaction with identifier \"%s\" is busy" ,
505+ gid )));
435506
436507if (user != gxact -> owner && !superuser_arg (user ))
437508ereport (ERROR ,
@@ -452,7 +523,8 @@ LockGXact(const char *gid, Oid user)
452523errhint ("Connect to the database where the transaction was prepared to finish it." )));
453524
454525/* OK for me to lock it */
455- gxact -> locking_xid = GetTopTransactionId ();
526+ gxact -> locking_backend = MyBackendId ;
527+ MyLockedGxact = gxact ;
456528
457529LWLockRelease (TwoPhaseStateLock );
458530
@@ -1100,6 +1172,13 @@ EndPrepare(GlobalTransaction gxact)
11001172 */
11011173MyPgXact -> inCommit = false;
11021174
1175+ /*
1176+ * Remember that we have this GlobalTransaction entry locked for us. If
1177+ * we crash after this point, it's too late to abort, but we must unlock
1178+ * it so that the prepared transaction can be committed or rolled back.
1179+ */
1180+ MyLockedGxact = gxact ;
1181+
11031182END_CRIT_SECTION ();
11041183
11051184/*
@@ -1346,8 +1425,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
13461425
13471426/*
13481427 * In case we fail while running the callbacks, mark the gxact invalid so
1349- * no one else will try to commit/rollback, and so it can be recycled
1350- * properly later. It is still locked by our XID so it won't go away yet.
1428+ * no one else will try to commit/rollback, and so it will be recycled
1429+ * if we fail after this point. It is still locked by our backend so it
1430+ * won't go away yet.
13511431 *
13521432 * (We assume it's safe to do this without taking TwoPhaseStateLock.)
13531433 */
@@ -1407,6 +1487,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
14071487RemoveTwoPhaseFile (xid , true);
14081488
14091489RemoveGXact (gxact );
1490+ MyLockedGxact = NULL ;
14101491
14111492pfree (buf );
14121493}