Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Misch2021-01-30 08:00:27 +0000
committerNoah Misch2021-01-30 08:04:05 +0000
commitd683d6528dba56669f24e5fac85c7692fc9ffd43 (patch)
tree785f53555d920df1aacb1c6ce96dd6e1a5ca3790 /src/backend
parentea3164aae4b2d2edfb1dc3bd450bfed86d3266ea (diff)
Fix CREATE INDEX CONCURRENTLY for simultaneous prepared transactions.
In a cluster having used CREATE INDEX CONCURRENTLY while having enabled prepared transactions, queries that use the resulting index can silently fail to find rows. Fix this for future CREATE INDEX CONCURRENTLY by making it wait for prepared transactions like it waits for ordinary transactions. This expands the VirtualTransactionId structure domain to admit prepared transactions. It may be necessary to reindex to recover from past occurrences. Back-patch to 9.5 (all supported versions). Andrey Borodin, reviewed (in earlier versions) by Tom Lane and Michael Paquier. Discussion: https://postgr.es/m/2E712143-97F7-4890-B470-4A35142ABC82@yandex-team.ru
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/storage/lmgr/lmgr.c3
-rw-r--r--src/backend/storage/lmgr/lock.c43
2 files changed, 26 insertions, 20 deletions
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 7b87002d7ce..f049f26ca96 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -846,8 +846,7 @@ WaitForLockersMultiple(List *locktags, LOCKMODE lockmode)
/*
* Note: GetLockConflicts() never reports our own xid, hence we need not
- * check for that. Also, prepared xacts are not reported, which is fine
- * since they certainly aren't going to do anything anymore.
+ * check for that. Also, prepared xacts are reported and awaited.
*/
/* Finally wait for each such transaction to complete */
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 98a2f366b4d..1c5ab8c0d97 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -2791,9 +2791,7 @@ FastPathGetRelationLockEntry(LOCALLOCK *locallock)
* so use of this function has to be thought about carefully.
*
* Note we never include the current xact's vxid in the result array,
- * since an xact never blocks itself. Also, prepared transactions are
- * ignored, which is a bit more debatable but is appropriate for current
- * uses of the result.
+ * since an xact never blocks itself.
*/
VirtualTransactionId *
GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
@@ -2818,19 +2816,21 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
/*
* Allocate memory to store results, and fill with InvalidVXID. We only
- * need enough space for MaxBackends + a terminator, since prepared xacts
- * don't count. InHotStandby allocate once in TopMemoryContext.
+ * need enough space for MaxBackends + max_prepared_xacts + a terminator.
+ * InHotStandby allocate once in TopMemoryContext.
*/
if (InHotStandby)
{
if (vxids == NULL)
vxids = (VirtualTransactionId *)
MemoryContextAlloc(TopMemoryContext,
- sizeof(VirtualTransactionId) * (MaxBackends + 1));
+ sizeof(VirtualTransactionId) *
+ (MaxBackends + max_prepared_xacts + 1));
}
else
vxids = (VirtualTransactionId *)
- palloc0(sizeof(VirtualTransactionId) * (MaxBackends + 1));
+ palloc0(sizeof(VirtualTransactionId) *
+ (MaxBackends + max_prepared_xacts + 1));
/* Compute hash code and partition lock, and look up conflicting modes. */
hashcode = LockTagHashCode(locktag);
@@ -2905,13 +2905,9 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
/* Conflict! */
GET_VXID_FROM_PGPROC(vxid, *proc);
- /*
- * If we see an invalid VXID, then either the xact has already
- * committed (or aborted), or it's a prepared xact. In either
- * case we may ignore it.
- */
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
+ /* else, xact already committed or aborted */
/* No need to examine remaining slots. */
break;
@@ -2968,11 +2964,6 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
GET_VXID_FROM_PGPROC(vxid, *proc);
- /*
- * If we see an invalid VXID, then either the xact has already
- * committed (or aborted), or it's a prepared xact. In either
- * case we may ignore it.
- */
if (VirtualTransactionIdIsValid(vxid))
{
int i;
@@ -2984,6 +2975,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
if (i >= fast_count)
vxids[count++] = vxid;
}
+ /* else, xact already committed or aborted */
}
}
@@ -2993,7 +2985,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
LWLockRelease(partitionLock);
- if (count > MaxBackends) /* should never happen */
+ if (count > MaxBackends + max_prepared_xacts) /* should never happen */
elog(PANIC, "too many conflicting locks found");
vxids[count].backendId = InvalidBackendId;
@@ -4349,6 +4341,21 @@ VirtualXactLock(VirtualTransactionId vxid, bool wait)
Assert(VirtualTransactionIdIsValid(vxid));
+ 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;
+ }
+
SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
/*