Skip to content

Commit f88d4cf

Browse files
committed
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
1 parent ea8c7e9 commit f88d4cf

File tree

9 files changed

+189
-34
lines changed

9 files changed

+189
-34
lines changed

src/backend/access/heap/heapam.c

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,12 @@ static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
105105
uint16 *new_infomask2);
106106
static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax,
107107
uint16 t_infomask);
108-
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
109-
int *remaining, uint16 infomask);
110-
static bool ConditionalMultiXactIdWait(MultiXactId multi,
111-
MultiXactStatus status, int *remaining,
112-
uint16 infomask);
108+
static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
109+
Relation rel, ItemPointer ctid, XLTW_Oper oper,
110+
int *remaining);
111+
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
112+
uint16 infomask, Relation rel, ItemPointer ctid,
113+
XLTW_Oper oper, int *remaining);
113114
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
114115
static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_modified,
115116
bool *copy);
@@ -2714,8 +2715,9 @@ heap_delete(Relation relation, ItemPointer tid,
27142715
if (infomask & HEAP_XMAX_IS_MULTI)
27152716
{
27162717
/* wait for multixact */
2717-
MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate,
2718-
NULL, infomask);
2718+
MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask,
2719+
relation, &tp.t_data->t_ctid, XLTW_Delete,
2720+
NULL);
27192721
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
27202722

27212723
/*
@@ -2741,7 +2743,7 @@ heap_delete(Relation relation, ItemPointer tid,
27412743
else
27422744
{
27432745
/* wait for regular transaction to end */
2744-
XactLockTableWait(xwait);
2746+
XactLockTableWait(xwait, relation, &tp.t_data->t_ctid, XLTW_Delete);
27452747
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
27462748

27472749
/*
@@ -3266,8 +3268,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
32663268
int remain;
32673269

32683270
/* wait for multixact */
3269-
MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain,
3270-
infomask);
3271+
MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask,
3272+
relation, &oldtup.t_data->t_ctid, XLTW_Update,
3273+
&remain);
32713274
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
32723275

32733276
/*
@@ -3341,7 +3344,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
33413344
else
33423345
{
33433346
/* wait for regular transaction to end */
3344-
XactLockTableWait(xwait);
3347+
XactLockTableWait(xwait, relation, &oldtup.t_data->t_ctid,
3348+
XLTW_Update);
33453349
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
33463350

33473351
/*
@@ -4402,14 +4406,18 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
44024406
if (nowait)
44034407
{
44044408
if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
4405-
status, NULL, infomask))
4409+
status, infomask, relation,
4410+
&tuple->t_data->t_ctid,
4411+
XLTW_Lock, NULL))
44064412
ereport(ERROR,
44074413
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
44084414
errmsg("could not obtain lock on row in relation \"%s\"",
44094415
RelationGetRelationName(relation))));
44104416
}
44114417
else
4412-
MultiXactIdWait((MultiXactId) xwait, status, NULL, infomask);
4418+
MultiXactIdWait((MultiXactId) xwait, status, infomask,
4419+
relation, &tuple->t_data->t_ctid,
4420+
XLTW_Lock, NULL);
44134421

44144422
/* if there are updates, follow the update chain */
44154423
if (follow_updates &&
@@ -4464,7 +4472,8 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
44644472
RelationGetRelationName(relation))));
44654473
}
44664474
else
4467-
XactLockTableWait(xwait);
4475+
XactLockTableWait(xwait, relation, &tuple->t_data->t_ctid,
4476+
XLTW_Lock);
44684477

44694478
/* if there are updates, follow the update chain */
44704479
if (follow_updates &&
@@ -5151,7 +5160,9 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
51515160
if (needwait)
51525161
{
51535162
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
5154-
XactLockTableWait(members[i].xid);
5163+
XactLockTableWait(members[i].xid, rel,
5164+
&mytup.t_data->t_ctid,
5165+
XLTW_LockUpdated);
51555166
pfree(members);
51565167
goto l4;
51575168
}
@@ -5211,7 +5222,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
52115222
if (needwait)
52125223
{
52135224
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
5214-
XactLockTableWait(rawxmax);
5225+
XactLockTableWait(rawxmax, rel, &mytup.t_data->t_ctid,
5226+
XLTW_LockUpdated);
52155227
goto l4;
52165228
}
52175229
if (res != HeapTupleMayBeUpdated)
@@ -6076,6 +6088,15 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
60766088
* Do_MultiXactIdWait
60776089
* Actual implementation for the two functions below.
60786090
*
6091+
* 'multi', 'status' and 'infomask' indicate what to sleep on (the status is
6092+
* needed to ensure we only sleep on conflicting members, and the infomask is
6093+
* used to optimize multixact access in case it's a lock-only multi); 'nowait'
6094+
* indicates whether to use conditional lock acquisition, to allow callers to
6095+
* fail if lock is unavailable. 'rel', 'ctid' and 'oper' are used to set up
6096+
* context information for error messages. 'remaining', if not NULL, receives
6097+
* the number of members that are still running, including any (non-aborted)
6098+
* subtransactions of our own transaction.
6099+
*
60796100
* We do this by sleeping on each member using XactLockTableWait. Any
60806101
* members that belong to the current backend are *not* waited for, however;
60816102
* this would not merely be useless but would lead to Assert failure inside
@@ -6093,7 +6114,9 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
60936114
*/
60946115
static bool
60956116
Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
6096-
int *remaining, uint16 infomask, bool nowait)
6117+
uint16 infomask, bool nowait,
6118+
Relation rel, ItemPointer ctid, XLTW_Oper oper,
6119+
int *remaining)
60976120
{
60986121
bool allow_old;
60996122
bool result = true;
@@ -6130,6 +6153,12 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
61306153
/*
61316154
* This member conflicts with our multi, so we have to sleep (or
61326155
* return failure, if asked to avoid waiting.)
6156+
*
6157+
* Note that we don't set up an error context callback ourselves,
6158+
* but instead we pass the info down to XactLockTableWait. This
6159+
* might seem a bit wasteful because the context is set up and
6160+
* tore down for each member of the multixact, but in reality it
6161+
* should be barely noticeable, and it avoids duplicate code.
61336162
*/
61346163
if (nowait)
61356164
{
@@ -6138,7 +6167,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
61386167
break;
61396168
}
61406169
else
6141-
XactLockTableWait(memxid);
6170+
XactLockTableWait(memxid, rel, ctid, oper);
61426171
}
61436172

61446173
pfree(members);
@@ -6159,13 +6188,14 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
61596188
*
61606189
* We return (in *remaining, if not NULL) the number of members that are still
61616190
* running, including any (non-aborted) subtransactions of our own transaction.
6162-
*
61636191
*/
61646192
static void
6165-
MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
6166-
int *remaining, uint16 infomask)
6193+
MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
6194+
Relation rel, ItemPointer ctid, XLTW_Oper oper,
6195+
int *remaining)
61676196
{
6168-
Do_MultiXactIdWait(multi, status, remaining, infomask, false);
6197+
(void) Do_MultiXactIdWait(multi, status, infomask, false,
6198+
rel, ctid, oper, remaining);
61696199
}
61706200

61716201
/*
@@ -6183,9 +6213,11 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
61836213
*/
61846214
static bool
61856215
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
6186-
int *remaining, uint16 infomask)
6216+
uint16 infomask, Relation rel, ItemPointer ctid,
6217+
XLTW_Oper oper, int *remaining)
61876218
{
6188-
return Do_MultiXactIdWait(multi, status, remaining, infomask, true);
6219+
return Do_MultiXactIdWait(multi, status, infomask, true,
6220+
rel, ctid, oper, remaining);
61896221
}
61906222

61916223
/*

src/backend/access/nbtree/nbtinsert.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ _bt_doinsert(Relation rel, IndexTuple itup,
168168
{
169169
/* Have to wait for the other guy ... */
170170
_bt_relbuf(rel, buf);
171-
XactLockTableWait(xwait);
171+
XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex);
172172
/* start over... */
173173
_bt_freestack(stack);
174174
goto top;

src/backend/catalog/index.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2295,7 +2295,9 @@ IndexBuildHeapScan(Relation heapRelation,
22952295
* Must drop the lock on the buffer before we wait
22962296
*/
22972297
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
2298-
XactLockTableWait(xwait);
2298+
XactLockTableWait(xwait, heapRelation,
2299+
&heapTuple->t_data->t_ctid,
2300+
XLTW_InsertIndexUnique);
22992301
goto recheck;
23002302
}
23012303
}
@@ -2341,7 +2343,9 @@ IndexBuildHeapScan(Relation heapRelation,
23412343
* Must drop the lock on the buffer before we wait
23422344
*/
23432345
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
2344-
XactLockTableWait(xwait);
2346+
XactLockTableWait(xwait, heapRelation,
2347+
&heapTuple->t_data->t_ctid,
2348+
XLTW_InsertIndexUnique);
23452349
goto recheck;
23462350
}
23472351

src/backend/executor/execMain.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1982,7 +1982,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
19821982
if (TransactionIdIsValid(SnapshotDirty.xmax))
19831983
{
19841984
ReleaseBuffer(buffer);
1985-
XactLockTableWait(SnapshotDirty.xmax);
1985+
XactLockTableWait(SnapshotDirty.xmax,
1986+
relation, &tuple.t_data->t_ctid,
1987+
XLTW_FetchUpdated);
19861988
continue; /* loop back to repeat heap_fetch */
19871989
}
19881990

src/backend/executor/execUtils.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,8 @@ check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo,
13071307
if (TransactionIdIsValid(xwait))
13081308
{
13091309
index_endscan(index_scan);
1310-
XactLockTableWait(xwait);
1310+
XactLockTableWait(xwait, heap, &tup->t_data->t_ctid,
1311+
XLTW_RecheckExclusionConstr);
13111312
goto retry;
13121313
}
13131314

src/backend/replication/logical/snapbuild.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1343,7 +1343,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
13431343
if (TransactionIdIsCurrentTransactionId(xid))
13441344
elog(ERROR, "waiting for ourselves");
13451345

1346-
XactLockTableWait(xid);
1346+
XactLockTableWait(xid, NULL, NULL, XLTW_None);
13471347
}
13481348

13491349
/* nothing could have built up so far, so don't perform cleanup */

src/backend/storage/lmgr/lmgr.c

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@
2525
#include "utils/inval.h"
2626

2727

28+
/*
29+
* Struct to hold context info for transaction lock waits.
30+
*
31+
* 'oper' is the operation that needs to wait for the other transaction; 'rel'
32+
* and 'ctid' specify the address of the tuple being waited for.
33+
*/
34+
typedef struct XactLockTableWaitInfo
35+
{
36+
XLTW_Oper oper;
37+
Relation rel;
38+
ItemPointer ctid;
39+
} XactLockTableWaitInfo;
40+
41+
static void XactLockTableWaitErrorCb(void *arg);
42+
2843
/*
2944
* RelationInitLockInfo
3045
* Initializes the lock information in a relation descriptor.
@@ -471,7 +486,9 @@ XactLockTableDelete(TransactionId xid)
471486
/*
472487
* XactLockTableWait
473488
*
474-
* Wait for the specified transaction to commit or abort.
489+
* Wait for the specified transaction to commit or abort. If an operation
490+
* is specified, an error context callback is set up. If 'oper' is passed as
491+
* None, no error context callback is set up.
475492
*
476493
* Note that this does the right thing for subtransactions: if we wait on a
477494
* subtransaction, we will exit as soon as it aborts or its top parent commits.
@@ -481,9 +498,31 @@ XactLockTableDelete(TransactionId xid)
481498
* and if so wait for its parent.
482499
*/
483500
void
484-
XactLockTableWait(TransactionId xid)
501+
XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid,
502+
XLTW_Oper oper)
485503
{
486504
LOCKTAG tag;
505+
XactLockTableWaitInfo info;
506+
ErrorContextCallback callback;
507+
508+
/*
509+
* If an operation is specified, set up our verbose error context
510+
* callback.
511+
*/
512+
if (oper != XLTW_None)
513+
{
514+
Assert(RelationIsValid(rel));
515+
Assert(ItemPointerIsValid(ctid));
516+
517+
info.rel = rel;
518+
info.ctid = ctid;
519+
info.oper = oper;
520+
521+
callback.callback = XactLockTableWaitErrorCb;
522+
callback.arg = &info;
523+
callback.previous = error_context_stack;
524+
error_context_stack = &callback;
525+
}
487526

488527
for (;;)
489528
{
@@ -500,6 +539,9 @@ XactLockTableWait(TransactionId xid)
500539
break;
501540
xid = SubTransGetParent(xid);
502541
}
542+
543+
if (oper != XLTW_None)
544+
error_context_stack = callback.previous;
503545
}
504546

505547
/*
@@ -533,6 +575,62 @@ ConditionalXactLockTableWait(TransactionId xid)
533575
return true;
534576
}
535577

578+
/*
579+
* XactLockTableWaitErrorContextCb
580+
* Error context callback for transaction lock waits.
581+
*/
582+
static void
583+
XactLockTableWaitErrorCb(void *arg)
584+
{
585+
XactLockTableWaitInfo *info = (XactLockTableWaitInfo *) arg;
586+
587+
/*
588+
* We would like to print schema name too, but that would require a
589+
* syscache lookup.
590+
*/
591+
if (info->oper != XLTW_None &&
592+
ItemPointerIsValid(info->ctid) && RelationIsValid(info->rel))
593+
{
594+
const char *cxt;
595+
596+
switch (info->oper)
597+
{
598+
case XLTW_Update:
599+
cxt = gettext_noop("while updating tuple (%u,%u) in relation \"%s\"");
600+
break;
601+
case XLTW_Delete:
602+
cxt = gettext_noop("while deleting tuple (%u,%u) in relation \"%s\"");
603+
break;
604+
case XLTW_Lock:
605+
cxt = gettext_noop("while locking tuple (%u,%u) in relation \"%s\"");
606+
break;
607+
case XLTW_LockUpdated:
608+
cxt = gettext_noop("while locking updated version (%u,%u) of tuple in relation \"%s\"");
609+
break;
610+
case XLTW_InsertIndex:
611+
cxt = gettext_noop("while inserting index tuple (%u,%u) in relation \"%s\"");
612+
break;
613+
case XLTW_InsertIndexUnique:
614+
cxt = gettext_noop("while checking uniqueness of tuple (%u,%u) in relation \"%s\"");
615+
break;
616+
case XLTW_FetchUpdated:
617+
cxt = gettext_noop("while rechecking updated tuple (%u,%u) in relation \"%s\"");
618+
break;
619+
case XLTW_RecheckExclusionConstr:
620+
cxt = gettext_noop("while checking exclusion constraint on tuple (%u,%u) in relation \"%s\"");
621+
break;
622+
623+
default:
624+
return;
625+
}
626+
627+
errcontext(cxt,
628+
ItemPointerGetBlockNumber(info->ctid),
629+
ItemPointerGetOffsetNumber(info->ctid),
630+
RelationGetRelationName(info->rel));
631+
}
632+
}
633+
536634
/*
537635
* WaitForLockersMultiple
538636
* Wait until no transaction holds locks that conflict with the given

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy