Skip to content

Commit

Permalink
Avoid serializability errors when locking a tuple with a committed up…
Browse files Browse the repository at this point in the history
…date

When key-share locking a tuple that has been not-key-updated, and the
update is a committed transaction, in some cases we raised
serializability errors:
    ERROR:  could not serialize access due to concurrent update

Because the key-share doesn't conflict with the update, the error is
unnecessary and inconsistent with the case that the update hasn't
committed yet.  This causes problems for some usage patterns, even if it
can be claimed that it's sufficient to retry the aborted transaction:
given a steady stream of updating transactions and a long locking
transaction, the long transaction can be starved indefinitely despite
multiple retries.

To fix, we recognize that HeapTupleSatisfiesUpdate can return
HeapTupleUpdated when an updating transaction has committed, and that we
need to deal with that case exactly as if it were a non-committed
update: verify whether the two operations conflict, and if not, carry on
normally.  If they do conflict, however, there is a difference: in the
HeapTupleBeingUpdated case we can just sleep until the concurrent
transaction is gone, while in the HeapTupleUpdated case this is not
possible and we must raise an error instead.

Per trouble report from Olivier Dony.

In addition to a couple of test cases that verify the changed behavior,
I added a test case to verify the behavior that remains unchanged,
namely that errors are raised when a update that modifies the key is
used.  That must still generate serializability errors.  One
pre-existing test case changes behavior; per discussion, the new
behavior is actually the desired one.

Discussion: https://www.postgresql.org/message-id/[email protected]
  https://www.postgresql.org/message-id/[email protected]

Backpatch to 9.3, where the problem appeared.
  • Loading branch information
alvherre committed Jul 15, 2016
1 parent 00f304c commit 533e9c6
Show file tree
Hide file tree
Showing 9 changed files with 1,401 additions and 4 deletions.
16 changes: 13 additions & 3 deletions src/backend/access/heap/heapam.c
Original file line number Diff line number Diff line change
Expand Up @@ -4540,7 +4540,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
*/
return HeapTupleInvisible;
}
else if (result == HeapTupleBeingUpdated)
else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
{
TransactionId xwait;
uint16 infomask;
Expand Down Expand Up @@ -4800,12 +4800,22 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
}

/*
* Time to sleep on the other transaction/multixact, if necessary.
*
* If the other transaction is an update that's already committed,
* then sleeping cannot possibly do any good: if we're required to
* sleep, get out to raise an error instead.
*
* By here, we either have already acquired the buffer exclusive lock,
* or we must wait for the locking transaction or multixact; so below
* we ensure that we grab buffer lock after the sleep.
*/

if (require_sleep)
if (require_sleep && result == HeapTupleUpdated)
{
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
goto failed;
}
else if (require_sleep)
{
/*
* Acquire tuple lock to establish our priority for the tuple, or
Expand Down
Loading

0 comments on commit 533e9c6

Please sign in to comment.