aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2021-01-30 00:00:27 -0800
committerNoah Misch <noah@leadboat.com>2021-01-30 00:00:42 -0800
commit86a5b309c9330661fd2c4c46e4dc7f07cca139e1 (patch)
tree0c4352cf050d719a723b619049aebaecbae84f18 /src/backend
parent2a01bc275be1a7d117944b1a58ea1fe5f6c377c6 (diff)
downloadpostgresql-86a5b309c9330661fd2c4c46e4dc7f07cca139e1.tar.gz
postgresql-86a5b309c9330661fd2c4c46e4dc7f07cca139e1.zip
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 7409de94059..cd29822d7a0 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -904,8 +904,7 @@ WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
/*
* 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 7fecb381625..03d7e21d37c 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -2904,9 +2904,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, int *countp)
@@ -2931,19 +2929,21 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
/*
* 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);
@@ -3018,13 +3018,9 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
/* 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;
@@ -3083,11 +3079,6 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
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;
@@ -3099,6 +3090,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
if (i >= fast_count)
vxids[count++] = vxid;
}
+ /* else, xact already committed or aborted */
}
}
@@ -3108,7 +3100,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
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;
@@ -4466,6 +4458,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);
/*