Skip to content

Commit 7bfaa46

Browse files
committed
Fix race condition in COMMIT PREPARED causing orphaned 2PC files
COMMIT PREPARED removes on-disk 2PC files near its end, but the state checked if a file is on-disk or not gets read from shared memory while not holding the two-phase state lock. Because of that, there was a small window where a second backend doing a PREPARE TRANSACTION could reuse the GlobalTransaction put back into the 2PC free list by the COMMIT PREPARED, overwriting the "ondisk" flag read afterwards by the COMMIT PREPARED to decide if its on-disk two-phase state file should be removed, preventing the file deletion. This commit fixes this issue so as the "ondisk" flag in the GlobalTransaction is read while holding the two-phase state lock, not from shared memory after its entry has been added to the free list. Orphaned two-phase state files flushed to disk after a checkpoint are discarded at the beginning of recovery. However, a truncation of pg_xact/ would make the startup process issue a FATAL when it cannot read the SLRU page holding the state of the transaction whose 2PC file was orphaned, which is a necessary step to decide if the 2PC file should be removed or not. Removing manually the file would be necessary in this case. Issue introduced by effe7d9, so backpatch all the way down. Mea culpa. Author: wuchengwen Discussion: https://postgr.es/m/tencent_A7F059B5136A359625C7B2E4A386B3C3F007@qq.com Backpatch-through: 12
1 parent 4f6d43c commit 7bfaa46

File tree

1 file changed

+8
-1
lines changed

1 file changed

+8
-1
lines changed

src/backend/access/transam/twophase.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
14871487
PGPROC *proc;
14881488
PGXACT *pgxact;
14891489
TransactionId xid;
1490+
bool ondisk;
14901491
char *buf;
14911492
char *bufptr;
14921493
TwoPhaseFileHeader *hdr;
@@ -1621,6 +1622,12 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
16211622

16221623
PredicateLockTwoPhaseFinish(xid, isCommit);
16231624

1625+
/*
1626+
* Read this value while holding the two-phase lock, as the on-disk 2PC
1627+
* file is physically removed after the lock is released.
1628+
*/
1629+
ondisk = gxact->ondisk;
1630+
16241631
/* Clear shared memory state */
16251632
RemoveGXact(gxact);
16261633

@@ -1636,7 +1643,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
16361643
/*
16371644
* And now we can clean up any files we may have left.
16381645
*/
1639-
if (gxact->ondisk)
1646+
if (ondisk)
16401647
RemoveTwoPhaseFile(xid, true);
16411648

16421649
MyLockedGxact = NULL;

0 commit comments

Comments
 (0)