From f11ba01cbfd0932d0c74f1bfdbfabb484bdfe943 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Wed, 21 Jul 2021 12:21:23 +0500
Subject: [PATCH v4 3/3] Wait on many xids returned from TwoPhaseGetXidByVXid()

---
 src/backend/access/transam/twophase.c | 18 +++++++++----
 src/backend/storage/lmgr/lock.c       | 38 ++++++++++++++++-----------
 src/include/access/twophase.h         | 12 ++++++++-
 3 files changed, 46 insertions(+), 22 deletions(-)

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index dba302d322..45e1c3259b 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -854,11 +854,13 @@ TwoPhaseGetGXact(TransactionId xid, bool lock_held)
  * TwoPhaseGetXidByVXid
  *		Try to lookup for vxid among prepared xacts
  */
-TransactionId
+XidListEntry
 TwoPhaseGetXidByVXid(VirtualTransactionId vxid)
 {
-	TransactionId result = InvalidTransactionId;
-	int			i;
+	int				i;
+	XidListEntry	result;
+	result.next = NULL;
+	result.xid = InvalidTransactionId;
 
 	LWLockAcquire(TwoPhaseStateLock, LW_SHARED);
 
@@ -871,8 +873,14 @@ TwoPhaseGetXidByVXid(VirtualTransactionId vxid)
 		if (proc->backendId == vxid.backendId &&
 				proc->lxid == vxid.localTransactionId)
 		{
-			result = gxact->xid;
-			break;
+			if (result.xid != InvalidTransactionId)
+			{
+				XidListEntry *copy = palloc(sizeof(XidListEntry));
+				copy->next = result.next;
+				copy->xid = result.xid;
+				result.next = copy;
+			}
+			result.xid = gxact->xid;
 		}
 	}
 
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 50553e729c..55efb33a7b 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -4472,33 +4472,39 @@ static bool PreparedXactLock(VirtualTransactionId vxid, TransactionId xid, bool
 {
 	LockAcquireResult	lar;
 	LOCKTAG				tag;
+	XidListEntry		xidlist;
 
 	/* Questionable heuristics */
 	if (max_prepared_xacts == 0)
 		return true;
 
-	if (!TransactionIdIsValid(xid))
+	if (TransactionIdIsValid(xid))
 	{
-		/* We must search for vxid in 2pc state */
+		xidlist.xid = xid;
+		xidlist.next = NULL;
+	}
+	else
+	{
+		/* We must search for vxids in 2pc state */
 		/* XXX: O(N*N) complexity where N is number of prepared xacts */
-		xid = TwoPhaseGetXidByVXid(vxid);
-		if (TransactionIdIsValid(xid))							// Remove this line
-		{														// Remove this line
-			elog(NOTICE,"XXX: Sucessfully found xid by vxid");	// Remove this line
-		}														// Remove this line
+		xidlist = TwoPhaseGetXidByVXid(vxid);
+		elog(NOTICE,"XXX: Sucessfully found xid by vxid");	// Remove this line
 	}
 
-	if (!TransactionIdIsValid(xid))
+	while (true)
 	{
-		/* Seems like vxid's 2pc is gone or never existed */
-		return true;
+		if (TransactionIdIsValid(xidlist.xid))
+		{
+			SET_LOCKTAG_TRANSACTION(tag, xid);
+			lar = LockAcquire(&tag, ShareLock, false, !wait);
+			if (lar != LOCKACQUIRE_NOT_AVAIL)
+				LockRelease(&tag, ShareLock, false);
+			return lar != LOCKACQUIRE_NOT_AVAIL;
+		}
+		if (xidlist.next == NULL)
+			return true;
+		xidlist = *xidlist.next;
 	}
-
-	SET_LOCKTAG_TRANSACTION(tag, xid);
-	lar = LockAcquire(&tag, ShareLock, false, !wait);
-	if (lar != LOCKACQUIRE_NOT_AVAIL)
-		LockRelease(&tag, ShareLock, false);
-	return lar != LOCKACQUIRE_NOT_AVAIL;
 }
 
 /*
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index 19923555ed..70b3cb9391 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -59,5 +59,15 @@ extern void PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
 extern void PrepareRedoRemove(TransactionId xid, bool giveWarning);
 extern void restoreTwoPhaseData(void);
 
-extern TransactionId TwoPhaseGetXidByVXid(VirtualTransactionId vxid);
+/* 
+ * This struc is expected to be used as list very rarely. Under normal
+ * circumstances TwoPhaseGetXidByVXid() returns only one xid.
+ * But under certain conditions can return many xids or nothing.
+ */
+typedef struct XidListEntry
+{
+	TransactionId xid;
+	struct XidListEntry* next;
+} XidListEntry;
+extern XidListEntry TwoPhaseGetXidByVXid(VirtualTransactionId vxid);
 #endif							/* TWOPHASE_H */
-- 
2.24.3 (Apple Git-128)

