Re: CREATE INDEX CONCURRENTLY does not index prepared xact's data
Andrey Borodin <x4mmm@yandex-team.ru>
From: Andrey Borodin <x4mmm@yandex-team.ru>
To: Noah Misch <noah@leadboat.com>
Cc: Michael Paquier <michael@paquier.xyz>,
Tom Lane <tgl@sss.pgh.pa.us>,
pgsql-bugs@lists.postgresql.org
Date: 2021-01-22T05:44:35Z
Lists: pgsql-bugs
Attachments
- v5-0001-Wait-for-prepared-xacts-in-CREATE-INDEX-CONCURREN.patch (application/octet-stream) patch v5-0001
- v5-0002-Add-test-for-CIC-with-prepared-xacts.patch (application/octet-stream) patch v5-0002
Thanks for looking into this!
> 17 янв. 2021 г., в 12:24, Noah Misch <noah@leadboat.com> написал(а):
>
>> --- a/src/backend/storage/lmgr/lock.c
>> +++ b/src/backend/storage/lmgr/lock.c
>> @@ -2931,15 +2929,17 @@ 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));
>
> PostgreSQL typically puts the operator before the newline. Also, please note
> the whitespace error that "git diff --check origin/master" reports.
Fixed.
>
>> }
>> else
>> vxids = (VirtualTransactionId *)
>
> This is updating only the InHotStandby branch. Both branches need the change.
Fixed.
>
>> @@ -4461,9 +4462,21 @@ bool
>> VirtualXactLock(VirtualTransactionId vxid, bool wait)
>> {
>> LOCKTAG tag;
>> - PGPROC *proc;
>> + PGPROC *proc = NULL;
>>
>> - Assert(VirtualTransactionIdIsValid(vxid));
>> + Assert(VirtualTransactionIdIsValid(vxid) ||
>> + VirtualTransactionIdIsPreparedXact(vxid));
>> +
>> + if (VirtualTransactionIdIsPreparedXact(vxid))
>> + {
>> + LockAcquireResult lar;
>> + /* If it's prepared xact - just wait for it */
>> + SET_LOCKTAG_TRANSACTION(tag, vxid.localTransactionId);
>> + lar = LockAcquire(&tag, ShareLock, false, !wait);
>> + if (lar == LOCKACQUIRE_OK)
>
> This should instead test "!= LOCKACQUIRE_NOT_AVAIL", lest
> LOCKACQUIRE_ALREADY_HELD happen. (It perhaps can't happen, but skipping the
> LockRelease() would be wrong if it did.)
I think that code that successfully acquired lock should release it. Other way we risk to release someone's else lock held for a reason. We only acquire lock to release it instantly anyway.
>
>> + LockRelease(&tag, ShareLock, false);
>> + return lar != LOCKACQUIRE_NOT_AVAIL;
>> + }
>>
>> SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
>>
>> diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
>> index 1c3e9c1999..cedb9d6d2a 100644
>> --- a/src/include/storage/lock.h
>> +++ b/src/include/storage/lock.h
>> @@ -70,6 +70,8 @@ typedef struct
>> #define VirtualTransactionIdIsValid(vxid) \
>> (((vxid).backendId != InvalidBackendId) && \
>> LocalTransactionIdIsValid((vxid).localTransactionId))
>> +#define VirtualTransactionIdIsPreparedXact(vxid) \
>> + ((vxid).backendId == InvalidBackendId)
>
> Your patch is introducing VirtualTransactionId values that represent prepared
> xacts, and it has VirtualTransactionIdIsValid() return false for those values.
> Let's make VirtualTransactionIdIsValid() return true for them, since they're
> as meaningful as any other value. The GetLockConflicts() header comment says
> "The result array is palloc'd and is terminated with an invalid VXID." Patch
> v4 falsifies that comment. The array can contain many of these new "invalid"
> VXIDs, and an all-zeroes VXID terminates the array. Rather than change the
> comment, let's change VirtualTransactionIdIsValid() to render the comment true
> again. Most (perhaps all) VirtualTransactionIdIsValid() callers won't need to
> distinguish the prepared-transaction case.
Makes sense, fixed. I was afraid that there's something I'm not aware about. I've iterated over VirtualTransactionIdIsValid() calls and did not find suspicious cases.
> An alternative to redefining VXID this way would be to have some new type,
> each instance of which holds either a valid VXID or a valid
> prepared-transaction XID. That alternative feels inferior to me, though.
> What do you think?
I think we should not call valid vxids "invalid".
By the way maybe rename "check-prepared-txns" to "check-prepared-xacts" for consistency?
Thanks!
Best regards, Andrey Borodin.
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Improve contrib/amcheck's tests for CREATE INDEX CONCURRENTLY.
- b1f943d2aa7e 14.1 landed
- 7f580aa5d88a 15.0 landed
- 5a4b8a8a720c 13.5 landed
- 141cd0ef0b0b 12.9 landed
-
Fix minor memory leaks in pg_dump.
- 70bef494000e 15.0 cited
-
Avoid race in RelationBuildDesc() affecting CREATE INDEX CONCURRENTLY.
- e428699cb3be 9.6.24 landed
- db86746fd11e 10.19 landed
- 5141e471b36a 11.14 landed
- dde966efb286 14.1 landed
- 2e33b43599ad 13.5 landed
- 0869e53d3a07 12.9 landed
- fdd965d074d4 15.0 landed
-
Fix CREATE INDEX CONCURRENTLY for the newest prepared transactions.
- df6158139f64 11.14 landed
- 560124a37c2f 10.19 landed
- 5184932432b4 9.6.24 landed
- fe5d44a1d38f 12.9 landed
- a9d0a5409415 13.5 landed
- a5b9a0000e14 14.1 landed
- 3cd9c3b92197 15.0 landed
-
Add -w back to the flags for pg_ctl (re)start in PostgresNode
- b33259e261ae 15.0 cited
-
Fix CREATE INDEX CONCURRENTLY for simultaneous prepared transactions.
- 77a0e14f0b21 9.5.25 landed
- d683d6528dba 9.6.21 landed
- 179775135b41 10.16 landed
- d1ab4bf6ed2d 11.11 landed
- be843ce29737 12.6 landed
- 86a5b309c933 13.2 landed
- 8a54e12a38d1 14.0 landed