aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/heap/heapam.c
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2016-07-15 14:17:20 -0400
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2016-07-15 14:17:20 -0400
commit649dd1b58b90754c67be86e99bd3af1bc3ab2c99 (patch)
tree24c48163e29e80cd110093cb9a12445e83483923 /src/backend/access/heap/heapam.c
parent3246135c478aac417aefb122d2299433eb1ff8f8 (diff)
downloadpostgresql-649dd1b58b90754c67be86e99bd3af1bc3ab2c99.tar.gz
postgresql-649dd1b58b90754c67be86e99bd3af1bc3ab2c99.zip
Avoid serializability errors when locking a tuple with a committed update
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/560AA479.4080807@odoo.com https://www.postgresql.org/message-id/20151014164844.3019.25750@wrigleys.postgresql.org Backpatch to 9.3, where the problem appeared.
Diffstat (limited to 'src/backend/access/heap/heapam.c')
-rw-r--r--src/backend/access/heap/heapam.c16
1 files changed, 13 insertions, 3 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 540c7ec0603..19b657d7022 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -4317,7 +4317,7 @@ l3:
*/
return HeapTupleInvisible;
}
- else if (result == HeapTupleBeingUpdated)
+ else if (result == HeapTupleBeingUpdated || result == HeapTupleUpdated)
{
TransactionId xwait;
uint16 infomask;
@@ -4577,12 +4577,22 @@ l3:
}
/*
+ * 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