5858#include "replication/walsender.h"
5959#include "replication/syncrep.h"
6060#include "storage/fd.h"
61+ #include "storage/ipc.h"
6162#include "storage/predicate.h"
6263#include "storage/proc.h"
6364#include "storage/procarray.h"
@@ -82,25 +83,25 @@ intmax_prepared_xacts = 0;
8283 *
8384 * The lifecycle of a global transaction is:
8485 *
85- * 1. After checking that the requested GID is not in use, set up an
86- *entry in the TwoPhaseState->prepXacts array with the correctXID andGID ,
87- *with locking_xid = my own XID and valid = false .
86+ * 1. After checking that the requested GID is not in use, set up an entry in
87+ * the TwoPhaseState->prepXacts array with the correctGID andvalid = false ,
88+ *and mark it as locked by my backend .
8889 *
8990 * 2. After successfully completing prepare, set valid = true and enter the
9091 * referenced PGPROC into the global ProcArray.
9192 *
92- * 3. To begin COMMIT PREPARED or ROLLBACK PREPARED, check that the entry
93- *is valid andits locking_xid is no longer active, then store my current
94- *XID intolocking_xid . This prevents concurrent attempts to commit or
95- * rollback the same prepared xact.
93+ * 3. To begin COMMIT PREPARED or ROLLBACK PREPARED, check that the entry is
94+ * valid andnot locked, then mark the entry as locked by storing my current
95+ *backend ID intolocking_backend . This prevents concurrent attempts to
96+ *commit or rollback the same prepared xact.
9697 *
9798 * 4. On completion of COMMIT PREPARED or ROLLBACK PREPARED, remove the entry
9899 * from the ProcArray and the TwoPhaseState->prepXacts array and return it to
99100 * the freelist.
100101 *
101102 * Note that if the preparing transaction fails between steps 1 and 2, the
102- * entrywill remain in prepXacts until recycled. We can detect recyclable
103- *entries by checking for valid = false and locking_xid no longer active .
103+ * entrymust be removed so that the GID and the GlobalTransaction struct
104+ *can be reused. See AtAbort_Twophase() .
104105 *
105106 * typedef struct GlobalTransactionData *GlobalTransaction appears in
106107 * twophase.h
@@ -115,8 +116,8 @@ typedef struct GlobalTransactionData
115116TimestampTz prepared_at ;/* time of preparation */
116117XLogRecPtr prepare_lsn ;/* XLOG offset of prepare record */
117118Oid owner ;/* ID of user that executed the xact */
118- TransactionId locking_xid ; /*top-level XID of backend working on xact */
119- bool valid ;/* TRUE iffully prepared */
119+ BackendId locking_backend ; /* backendcurrently working on the xact */
120+ bool valid ;/* TRUE ifPGPROC entry is in proc array */
120121char gid [GIDSIZE ];/* The GID assigned to the prepared xact */
121122}GlobalTransactionData ;
122123
@@ -141,6 +142,12 @@ typedef struct TwoPhaseStateData
141142
142143static TwoPhaseStateData * TwoPhaseState ;
143144
145+ /*
146+ * Global transaction entry currently locked by us, if any.
147+ */
148+ static GlobalTransaction MyLockedGxact = NULL ;
149+
150+ static bool twophaseExitRegistered = false;
144151
145152static void RecordTransactionCommitPrepared (TransactionId xid ,
146153int nchildren ,
@@ -157,6 +164,7 @@ static void RecordTransactionAbortPrepared(TransactionId xid,
157164RelFileNode * rels );
158165static void ProcessRecords (char * bufptr ,TransactionId xid ,
159166const TwoPhaseCallback callbacks []);
167+ static void RemoveGXact (GlobalTransaction gxact );
160168
161169
162170/*
@@ -230,6 +238,74 @@ TwoPhaseShmemInit(void)
230238Assert (found );
231239}
232240
241+ /*
242+ * Exit hook to unlock the global transaction entry we're working on.
243+ */
244+ static void
245+ AtProcExit_Twophase (int code ,Datum arg )
246+ {
247+ /* same logic as abort */
248+ AtAbort_Twophase ();
249+ }
250+
251+ /*
252+ * Abort hook to unlock the global transaction entry we're working on.
253+ */
254+ void
255+ AtAbort_Twophase (void )
256+ {
257+ if (MyLockedGxact == NULL )
258+ return ;
259+
260+ /*
261+ * What to do with the locked global transaction entry? If we were in
262+ * the process of preparing the transaction, but haven't written the WAL
263+ * record and state file yet, the transaction must not be considered as
264+ * prepared. Likewise, if we are in the process of finishing an
265+ * already-prepared transaction, and fail after having already written
266+ * the 2nd phase commit or rollback record to the WAL, the transaction
267+ * should not be considered as prepared anymore. In those cases, just
268+ * remove the entry from shared memory.
269+ *
270+ * Otherwise, the entry must be left in place so that the transaction
271+ * can be finished later, so just unlock it.
272+ *
273+ * If we abort during prepare, after having written the WAL record, we
274+ * might not have transfered all locks and other state to the prepared
275+ * transaction yet. Likewise, if we abort during commit or rollback,
276+ * after having written the WAL record, we might not have released
277+ * all the resources held by the transaction yet. In those cases, the
278+ * in-memory state can be wrong, but it's too late to back out.
279+ */
280+ if (!MyLockedGxact -> valid )
281+ {
282+ RemoveGXact (MyLockedGxact );
283+ }
284+ else
285+ {
286+ LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
287+
288+ MyLockedGxact -> locking_backend = InvalidBackendId ;
289+
290+ LWLockRelease (TwoPhaseStateLock );
291+ }
292+ MyLockedGxact = NULL ;
293+ }
294+
295+ /*
296+ * This is called after we have finished transfering state to the prepared
297+ * PGXACT entry.
298+ */
299+ void
300+ PostPrepare_Twophase ()
301+ {
302+ LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
303+ MyLockedGxact -> locking_backend = InvalidBackendId ;
304+ LWLockRelease (TwoPhaseStateLock );
305+
306+ MyLockedGxact = NULL ;
307+ }
308+
233309
234310/*
235311 * MarkAsPreparing
@@ -261,29 +337,15 @@ MarkAsPreparing(TransactionId xid, const char *gid,
261337errmsg ("prepared transactions are disabled" ),
262338errhint ("Set max_prepared_transactions to a nonzero value." )));
263339
264- LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
265-
266- /*
267- * First, find and recycle any gxacts that failed during prepare. We do
268- * this partly to ensure we don't mistakenly say their GIDs are still
269- * reserved, and partly so we don't fail on out-of-slots unnecessarily.
270- */
271- for (i = 0 ;i < TwoPhaseState -> numPrepXacts ;i ++ )
340+ /* on first call, register the exit hook */
341+ if (!twophaseExitRegistered )
272342{
273- gxact = TwoPhaseState -> prepXacts [i ];
274- if (!gxact -> valid && !TransactionIdIsActive (gxact -> locking_xid ))
275- {
276- /* It's dead Jim ... remove from the active array */
277- TwoPhaseState -> numPrepXacts -- ;
278- TwoPhaseState -> prepXacts [i ]= TwoPhaseState -> prepXacts [TwoPhaseState -> numPrepXacts ];
279- /* and put it back in the freelist */
280- gxact -> next = TwoPhaseState -> freeGXacts ;
281- TwoPhaseState -> freeGXacts = gxact ;
282- /* Back up index count too, so we don't miss scanning one */
283- i -- ;
284- }
343+ before_shmem_exit (AtProcExit_Twophase ,0 );
344+ twophaseExitRegistered = true;
285345}
286346
347+ LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
348+
287349/* Check for conflicting GID */
288350for (i = 0 ;i < TwoPhaseState -> numPrepXacts ;i ++ )
289351{
@@ -340,14 +402,20 @@ MarkAsPreparing(TransactionId xid, const char *gid,
340402/* initialize LSN to 0 (start of WAL) */
341403gxact -> prepare_lsn = 0 ;
342404gxact -> owner = owner ;
343- gxact -> locking_xid = xid ;
405+ gxact -> locking_backend = MyBackendId ;
344406gxact -> valid = false;
345407strcpy (gxact -> gid ,gid );
346408
347409/* And insert it into the active array */
348410Assert (TwoPhaseState -> numPrepXacts < max_prepared_xacts );
349411TwoPhaseState -> prepXacts [TwoPhaseState -> numPrepXacts ++ ]= gxact ;
350412
413+ /*
414+ * Remember that we have this GlobalTransaction entry locked for us.
415+ * If we abort after this, we must release it.
416+ */
417+ MyLockedGxact = gxact ;
418+
351419LWLockRelease (TwoPhaseStateLock );
352420
353421return gxact ;
@@ -410,6 +478,13 @@ LockGXact(const char *gid, Oid user)
410478{
411479int i ;
412480
481+ /* on first call, register the exit hook */
482+ if (!twophaseExitRegistered )
483+ {
484+ before_shmem_exit (AtProcExit_Twophase ,0 );
485+ twophaseExitRegistered = true;
486+ }
487+
413488LWLockAcquire (TwoPhaseStateLock ,LW_EXCLUSIVE );
414489
415490for (i = 0 ;i < TwoPhaseState -> numPrepXacts ;i ++ )
@@ -424,15 +499,11 @@ LockGXact(const char *gid, Oid user)
424499continue ;
425500
426501/* Found it, but has someone else got it locked? */
427- if (TransactionIdIsValid (gxact -> locking_xid ))
428- {
429- if (TransactionIdIsActive (gxact -> locking_xid ))
430- ereport (ERROR ,
431- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
432- errmsg ("prepared transaction with identifier \"%s\" is busy" ,
433- gid )));
434- gxact -> locking_xid = InvalidTransactionId ;
435- }
502+ if (gxact -> locking_backend != InvalidBackendId )
503+ ereport (ERROR ,
504+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
505+ errmsg ("prepared transaction with identifier \"%s\" is busy" ,
506+ gid )));
436507
437508if (user != gxact -> owner && !superuser_arg (user ))
438509ereport (ERROR ,
@@ -453,7 +524,8 @@ LockGXact(const char *gid, Oid user)
453524errhint ("Connect to the database where the transaction was prepared to finish it." )));
454525
455526/* OK for me to lock it */
456- gxact -> locking_xid = GetTopTransactionId ();
527+ gxact -> locking_backend = MyBackendId ;
528+ MyLockedGxact = gxact ;
457529
458530LWLockRelease (TwoPhaseStateLock );
459531
@@ -1089,6 +1161,13 @@ EndPrepare(GlobalTransaction gxact)
10891161 */
10901162MyPgXact -> delayChkpt = false;
10911163
1164+ /*
1165+ * Remember that we have this GlobalTransaction entry locked for us. If
1166+ * we crash after this point, it's too late to abort, but we must unlock
1167+ * it so that the prepared transaction can be committed or rolled back.
1168+ */
1169+ MyLockedGxact = gxact ;
1170+
10921171END_CRIT_SECTION ();
10931172
10941173/*
@@ -1335,8 +1414,9 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
13351414
13361415/*
13371416 * In case we fail while running the callbacks, mark the gxact invalid so
1338- * no one else will try to commit/rollback, and so it can be recycled
1339- * properly later. It is still locked by our XID so it won't go away yet.
1417+ * no one else will try to commit/rollback, and so it will be recycled
1418+ * if we fail after this point. It is still locked by our backend so it
1419+ * won't go away yet.
13401420 *
13411421 * (We assume it's safe to do this without taking TwoPhaseStateLock.)
13421422 */
@@ -1396,6 +1476,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
13961476RemoveTwoPhaseFile (xid , true);
13971477
13981478RemoveGXact (gxact );
1479+ MyLockedGxact = NULL ;
13991480
14001481pfree (buf );
14011482}