Allow special SKIP LOCKED condition in Assert()
authorAlvaro Herrera
Tue, 4 Jan 2022 16:01:05 +0000 (13:01 -0300)
committerAlvaro Herrera
Tue, 4 Jan 2022 16:01:05 +0000 (13:01 -0300)
Under concurrency, it is possible for two sessions to be merrily locking
and releasing a tuple and marking it again as HEAP_XMAX_INVALID all the
while a third session attempts to lock it, miserably fails at it, and
then contemplates life, the universe and everything only to eventually
fail an assertion that said bit is not set.  Before SKIP LOCKED that was
indeed a reasonable expectation, but alas! commit df630b0dd5ea falsified
it.

This bug is as old as time itself, and even older, if you think time
begins with the oldest supported branch.  Therefore, backpatch to all
supported branches.

Author: Simon Riggs 
Discussion: https://postgr.es/m/CANbhV-FeEwMnN8yuMyss7if1ZKjOKfjcgqB26n8pqu1e=q0ebg@mail.gmail.com

src/backend/access/heap/heapam.c

index 94edb1decaee69d360c611b5d537b58b0bb4d820..827c43e0ccb93c6efc731b86eb7a917f7a1a2d5e 100644 (file)
@@ -4497,7 +4497,15 @@ failed:
    {
        Assert(result == TM_SelfModified || result == TM_Updated ||
               result == TM_Deleted || result == TM_WouldBlock);
-       Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
+
+       /*
+        * When locking a tuple under LockWaitSkip semantics and we fail with
+        * TM_WouldBlock above, it's possible for concurrent transactions to
+        * release the lock and set HEAP_XMAX_INVALID in the meantime.  So
+        * this assert is slightly different from the equivalent one in
+        * heap_delete and heap_update.
+        */
+       Assert(TM_WouldBlock || !(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
        Assert(result != TM_Updated ||
               !ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
        tmfd->ctid = tuple->t_data->t_ctid;