From 2485aa28e16d9bcd7f96c2b6acd41c2f3edf86b3 Mon Sep 17 00:00:00 2001
From: Maksim Melnikov <m.melnikov@postgrespro.ru>
Date: Thu, 10 Sep 2026 19:37:16 +0300
Subject: [PATCH v2] Fixing race between prepared transaction commit and
 checkpointer.

---
 src/backend/access/transam/twophase.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 48e478a4ecb..9435204313c 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -1531,6 +1531,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	fxid = gxact->fxid;
 	xid = XidFromFullTransactionId(fxid);
 
+	LWLockAcquire(TwoPhaseStateLock, LW_SHARED);
 	/*
 	 * Read and validate 2PC state data. State data will typically be stored
 	 * in WAL files if the LSN is after the last checkpoint record, or moved
@@ -1541,6 +1542,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	else
 		XlogReadTwoPhaseData(gxact->prepare_start_lsn, &buf, NULL);
 
+	LWLockRelease(TwoPhaseStateLock);
 
 	/*
 	 * Disassemble the header area
@@ -1853,7 +1855,7 @@ CheckPointTwoPhase(XLogRecPtr redo_horizon)
 	 * prepare_end_lsn set prior to the last checkpoint yet is marked invalid,
 	 * because of the efforts with delayChkptFlags.
 	 */
-	LWLockAcquire(TwoPhaseStateLock, LW_SHARED);
+	LWLockAcquire(TwoPhaseStateLock, LW_EXCLUSIVE);
 	for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
 	{
 		/*
-- 
2.43.0

