v6-0002-Fix-CREATE-INDEX-CONCURRENTLY-in-precence-of-vxid.patch
application/octet-stream
Filename: v6-0002-Fix-CREATE-INDEX-CONCURRENTLY-in-precence-of-vxid.patch
Type: application/octet-stream
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v6-0002
Subject: Fix CREATE INDEX CONCURRENTLY in precence of vxids converted to 2pc
| File | + | − |
|---|---|---|
| src/backend/access/transam/twophase.c | 46 | 3 |
| src/backend/storage/lmgr/lock.c | 76 | 20 |
| src/include/access/twophase.h | 12 | 0 |
From 515f3cd1081880b72a230487a2a9002a5e56fcdc Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Mon, 19 Jul 2021 11:50:02 +0500
Subject: [PATCH v6 2/3] Fix CREATE INDEX CONCURRENTLY in precence of vxids
converted to 2pc
---
src/backend/access/transam/twophase.c | 49 +++++++++++++-
src/backend/storage/lmgr/lock.c | 96 +++++++++++++++++++++------
src/include/access/twophase.h | 12 ++++
3 files changed, 134 insertions(+), 23 deletions(-)
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index f67d813c56..45e1c3259b 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -373,8 +373,10 @@ MarkAsPreparing(TransactionId xid, const char *gid,
TimestampTz prepared_at, Oid owner, Oid databaseid)
{
GlobalTransaction gxact;
+
int i;
+
if (strlen(gid) >= GIDSIZE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -459,14 +461,16 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
proc->pgprocno = gxact->pgprocno;
SHMQueueElemInit(&(proc->links));
proc->waitStatus = PROC_WAIT_STATUS_OK;
- /* We set up the gxact's VXID as InvalidBackendId/XID */
- proc->lxid = (LocalTransactionId) xid;
+
+ /* We set up the gxact's VXID as real for CIC purposes */
+ proc->lxid = MyProc->lxid;
proc->xid = xid;
Assert(proc->xmin == InvalidTransactionId);
proc->delayChkpt = false;
proc->statusFlags = 0;
proc->pid = 0;
- proc->backendId = InvalidBackendId;
+ /* May be backendId of startup process */
+ proc->backendId = MyBackendId;
proc->databaseId = databaseid;
proc->roleId = owner;
proc->tempNamespaceId = InvalidOid;
@@ -846,6 +850,45 @@ TwoPhaseGetGXact(TransactionId xid, bool lock_held)
return result;
}
+/*
+ * TwoPhaseGetXidByVXid
+ * Try to lookup for vxid among prepared xacts
+ */
+XidListEntry
+TwoPhaseGetXidByVXid(VirtualTransactionId vxid)
+{
+ int i;
+ XidListEntry result;
+ result.next = NULL;
+ result.xid = InvalidTransactionId;
+
+ LWLockAcquire(TwoPhaseStateLock, LW_SHARED);
+
+ for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
+ {
+ GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
+ PGPROC *proc;
+ proc = &ProcGlobal->allProcs[gxact->pgprocno];
+
+ if (proc->backendId == vxid.backendId &&
+ proc->lxid == vxid.localTransactionId)
+ {
+ if (result.xid != InvalidTransactionId)
+ {
+ XidListEntry *copy = palloc(sizeof(XidListEntry));
+ copy->next = result.next;
+ copy->xid = result.xid;
+ result.next = copy;
+ }
+ result.xid = gxact->xid;
+ }
+ }
+
+ LWLockRelease(TwoPhaseStateLock);
+
+ return result;
+}
+
/*
* TwoPhaseGetDummyBackendId
* Get the dummy backend ID for prepared transaction specified by XID
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 108b4d9023..55efb33a7b 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -386,6 +386,7 @@ static void LockRefindAndRelease(LockMethod lockMethodTable, PGPROC *proc,
bool decrement_strong_lock_count);
static void GetSingleProcBlockerStatusData(PGPROC *blocked_proc,
BlockedProcsData *data);
+static bool PreparedXactLock(VirtualTransactionId vxid, TransactionId xid, bool wait);
/*
@@ -3017,7 +3018,14 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
/* Conflict! */
GET_VXID_FROM_PGPROC(vxid, *proc);
- if (VirtualTransactionIdIsValid(vxid))
+ /* Prefer real Xid over local Xid */
+ if (TransactionIdIsValid(proc->xid))
+ {
+ vxids[count].backendId = InvalidBackendId;
+ vxids[count].localTransactionId = proc->xid;
+ count++;
+ }
+ else if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
/* else, xact already committed or aborted */
@@ -3078,7 +3086,14 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
GET_VXID_FROM_PGPROC(vxid, *proc);
- if (VirtualTransactionIdIsValid(vxid))
+ /* Prefer real Xid over local Xid */
+ if (TransactionIdIsValid(proc->xid))
+ {
+ vxids[count].backendId = InvalidBackendId;
+ vxids[count].localTransactionId = proc->xid;
+ count++;
+ }
+ else if (VirtualTransactionIdIsValid(vxid))
{
int i;
@@ -4447,6 +4462,51 @@ VirtualXactLockTableCleanup(void)
}
}
+/*
+ * PreparedXactLock
+ *
+ * Wait for xid completition if have xid. Otherwise try to find xid among
+ * fake procarray entries.
+ */
+static bool PreparedXactLock(VirtualTransactionId vxid, TransactionId xid, bool wait)
+{
+ LockAcquireResult lar;
+ LOCKTAG tag;
+ XidListEntry xidlist;
+
+ /* Questionable heuristics */
+ if (max_prepared_xacts == 0)
+ return true;
+
+ if (TransactionIdIsValid(xid))
+ {
+ 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 */
+ xidlist = TwoPhaseGetXidByVXid(vxid);
+ elog(NOTICE,"XXX: Sucessfully found xid by vxid"); // Remove this line
+ }
+
+ while (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;
+ }
+}
+
/*
* VirtualXactLock
*
@@ -4459,25 +4519,18 @@ VirtualXactLockTableCleanup(void)
bool
VirtualXactLock(VirtualTransactionId vxid, bool wait)
{
- LOCKTAG tag;
- PGPROC *proc;
+ LOCKTAG tag;
+ PGPROC *proc;
+ TransactionId xid = InvalidTransactionId;
Assert(VirtualTransactionIdIsValid(vxid));
+ /*
+ * Already prepared transactions don't hold vxid locks. The
+ * LocalTransactionId is always a normal, locked XID.
+ */
if (VirtualTransactionIdIsPreparedXact(vxid))
- {
- LockAcquireResult lar;
-
- /*
- * Prepared transactions don't hold vxid locks. The
- * LocalTransactionId is always a normal, locked XID.
- */
- SET_LOCKTAG_TRANSACTION(tag, vxid.localTransactionId);
- lar = LockAcquire(&tag, ShareLock, false, !wait);
- if (lar != LOCKACQUIRE_NOT_AVAIL)
- LockRelease(&tag, ShareLock, false);
- return lar != LOCKACQUIRE_NOT_AVAIL;
- }
+ return PreparedXactLock(vxid, vxid.localTransactionId, wait);
SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
@@ -4491,7 +4544,7 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
*/
proc = BackendIdGetProc(vxid.backendId);
if (proc == NULL)
- return true;
+ return PreparedXactLock(vxid, InvalidTransactionId, wait);
/*
* We must acquire this lock before checking the backendId and lxid
@@ -4505,9 +4558,12 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
|| proc->fpLocalTransactionId != vxid.localTransactionId)
{
LWLockRelease(&proc->fpInfoLock);
- return true;
+ return PreparedXactLock(vxid, InvalidTransactionId, wait);
}
+ /* Save the xid to test if transaction coverted to 2pc later */
+ xid = proc->xid;
+
/*
* If we aren't asked to wait, there's no need to set up a lock table
* entry. The transaction is still in progress, so just return false.
@@ -4559,7 +4615,7 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
(void) LockAcquire(&tag, ShareLock, false, false);
LockRelease(&tag, ShareLock, false);
- return true;
+ return PreparedXactLock(vxid, xid, wait);;
}
/*
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index 91786da784..70b3cb9391 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -58,4 +58,16 @@ extern void PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
XLogRecPtr end_lsn, RepOriginId origin_id);
extern void PrepareRedoRemove(TransactionId xid, bool giveWarning);
extern void restoreTwoPhaseData(void);
+
+/*
+ * 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)