diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2014-03-19 15:10:36 -0300 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2014-03-19 15:10:36 -0300 |
commit | f88d4cfc9d417dac2ee41a8f5e593898e56fd2bd (patch) | |
tree | 225a86782144a53e83e6083e92df29c8d0785007 /src/backend/access/heap/heapam.c | |
parent | ea8c7e9054abf23fa3de2f8e4414f60ac8a8b620 (diff) | |
download | postgresql-f88d4cfc9d417dac2ee41a8f5e593898e56fd2bd.tar.gz postgresql-f88d4cfc9d417dac2ee41a8f5e593898e56fd2bd.zip |
Setup error context callback for transaction lock waits
With this in place, a session blocking behind another one because of
tuple locks will get a context line mentioning the relation name, tuple
TID, and operation being done on tuple. For example:
LOG: process 11367 still waiting for ShareLock on transaction 717 after 1000.108 ms
DETAIL: Process holding the lock: 11366. Wait queue: 11367.
CONTEXT: while updating tuple (0,2) in relation "foo"
STATEMENT: UPDATE foo SET value = 3;
Most usefully, the new line is displayed by log entries due to
log_lock_waits, although of course it will be printed by any other log
message as well.
Author: Christian Kruse, some tweaks by Álvaro Herrera
Reviewed-by: Amit Kapila, Andres Freund, Tom Lane, Robert Haas
Diffstat (limited to 'src/backend/access/heap/heapam.c')
-rw-r--r-- | src/backend/access/heap/heapam.c | 80 |
1 files changed, 56 insertions, 24 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index e2337acc2ab..180eff4e6b0 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -105,11 +105,12 @@ static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask, uint16 *new_infomask2); static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask); -static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, - int *remaining, uint16 infomask); -static bool ConditionalMultiXactIdWait(MultiXactId multi, - MultiXactStatus status, int *remaining, - uint16 infomask); +static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, + Relation rel, ItemPointer ctid, XLTW_Oper oper, + int *remaining); +static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, + uint16 infomask, Relation rel, ItemPointer ctid, + XLTW_Oper oper, int *remaining); static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup); static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified, bool *copy); @@ -2714,8 +2715,9 @@ l1: if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ - MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, - NULL, infomask); + MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask, + relation, &tp.t_data->t_ctid, XLTW_Delete, + NULL); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* @@ -2741,7 +2743,7 @@ l1: else { /* wait for regular transaction to end */ - XactLockTableWait(xwait); + XactLockTableWait(xwait, relation, &tp.t_data->t_ctid, XLTW_Delete); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* @@ -3266,8 +3268,9 @@ l2: int remain; /* wait for multixact */ - MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain, - infomask); + MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask, + relation, &oldtup.t_data->t_ctid, XLTW_Update, + &remain); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* @@ -3341,7 +3344,8 @@ l2: else { /* wait for regular transaction to end */ - XactLockTableWait(xwait); + XactLockTableWait(xwait, relation, &oldtup.t_data->t_ctid, + XLTW_Update); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* @@ -4402,14 +4406,18 @@ l3: if (nowait) { if (!ConditionalMultiXactIdWait((MultiXactId) xwait, - status, NULL, infomask)) + status, infomask, relation, + &tuple->t_data->t_ctid, + XLTW_Lock, NULL)) ereport(ERROR, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), errmsg("could not obtain lock on row in relation \"%s\"", RelationGetRelationName(relation)))); } else - MultiXactIdWait((MultiXactId) xwait, status, NULL, infomask); + MultiXactIdWait((MultiXactId) xwait, status, infomask, + relation, &tuple->t_data->t_ctid, + XLTW_Lock, NULL); /* if there are updates, follow the update chain */ if (follow_updates && @@ -4464,7 +4472,8 @@ l3: RelationGetRelationName(relation)))); } else - XactLockTableWait(xwait); + XactLockTableWait(xwait, relation, &tuple->t_data->t_ctid, + XLTW_Lock); /* if there are updates, follow the update chain */ if (follow_updates && @@ -5151,7 +5160,9 @@ l4: if (needwait) { LockBuffer(buf, BUFFER_LOCK_UNLOCK); - XactLockTableWait(members[i].xid); + XactLockTableWait(members[i].xid, rel, + &mytup.t_data->t_ctid, + XLTW_LockUpdated); pfree(members); goto l4; } @@ -5211,7 +5222,8 @@ l4: if (needwait) { LockBuffer(buf, BUFFER_LOCK_UNLOCK); - XactLockTableWait(rawxmax); + XactLockTableWait(rawxmax, rel, &mytup.t_data->t_ctid, + XLTW_LockUpdated); goto l4; } if (res != HeapTupleMayBeUpdated) @@ -6076,6 +6088,15 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple) * Do_MultiXactIdWait * Actual implementation for the two functions below. * + * 'multi', 'status' and 'infomask' indicate what to sleep on (the status is + * needed to ensure we only sleep on conflicting members, and the infomask is + * used to optimize multixact access in case it's a lock-only multi); 'nowait' + * indicates whether to use conditional lock acquisition, to allow callers to + * fail if lock is unavailable. 'rel', 'ctid' and 'oper' are used to set up + * context information for error messages. 'remaining', if not NULL, receives + * the number of members that are still running, including any (non-aborted) + * subtransactions of our own transaction. + * * We do this by sleeping on each member using XactLockTableWait. Any * members that belong to the current backend are *not* waited for, however; * this would not merely be useless but would lead to Assert failure inside @@ -6093,7 +6114,9 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple) */ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, - int *remaining, uint16 infomask, bool nowait) + uint16 infomask, bool nowait, + Relation rel, ItemPointer ctid, XLTW_Oper oper, + int *remaining) { bool allow_old; bool result = true; @@ -6130,6 +6153,12 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, /* * This member conflicts with our multi, so we have to sleep (or * return failure, if asked to avoid waiting.) + * + * Note that we don't set up an error context callback ourselves, + * but instead we pass the info down to XactLockTableWait. This + * might seem a bit wasteful because the context is set up and + * tore down for each member of the multixact, but in reality it + * should be barely noticeable, and it avoids duplicate code. */ if (nowait) { @@ -6138,7 +6167,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, break; } else - XactLockTableWait(memxid); + XactLockTableWait(memxid, rel, ctid, oper); } pfree(members); @@ -6159,13 +6188,14 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, * * We return (in *remaining, if not NULL) the number of members that are still * running, including any (non-aborted) subtransactions of our own transaction. - * */ static void -MultiXactIdWait(MultiXactId multi, MultiXactStatus status, - int *remaining, uint16 infomask) +MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, + Relation rel, ItemPointer ctid, XLTW_Oper oper, + int *remaining) { - Do_MultiXactIdWait(multi, status, remaining, infomask, false); + (void) Do_MultiXactIdWait(multi, status, infomask, false, + rel, ctid, oper, remaining); } /* @@ -6183,9 +6213,11 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, */ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, - int *remaining, uint16 infomask) + uint16 infomask, Relation rel, ItemPointer ctid, + XLTW_Oper oper, int *remaining) { - return Do_MultiXactIdWait(multi, status, remaining, infomask, true); + return Do_MultiXactIdWait(multi, status, infomask, true, + rel, ctid, oper, remaining); } /* |