mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
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
This commit is contained in:
@@ -25,6 +25,21 @@
|
||||
#include "utils/inval.h"
|
||||
|
||||
|
||||
/*
|
||||
* Struct to hold context info for transaction lock waits.
|
||||
*
|
||||
* 'oper' is the operation that needs to wait for the other transaction; 'rel'
|
||||
* and 'ctid' specify the address of the tuple being waited for.
|
||||
*/
|
||||
typedef struct XactLockTableWaitInfo
|
||||
{
|
||||
XLTW_Oper oper;
|
||||
Relation rel;
|
||||
ItemPointer ctid;
|
||||
} XactLockTableWaitInfo;
|
||||
|
||||
static void XactLockTableWaitErrorCb(void *arg);
|
||||
|
||||
/*
|
||||
* RelationInitLockInfo
|
||||
* Initializes the lock information in a relation descriptor.
|
||||
@@ -471,7 +486,9 @@ XactLockTableDelete(TransactionId xid)
|
||||
/*
|
||||
* XactLockTableWait
|
||||
*
|
||||
* Wait for the specified transaction to commit or abort.
|
||||
* Wait for the specified transaction to commit or abort. If an operation
|
||||
* is specified, an error context callback is set up. If 'oper' is passed as
|
||||
* None, no error context callback is set up.
|
||||
*
|
||||
* Note that this does the right thing for subtransactions: if we wait on a
|
||||
* subtransaction, we will exit as soon as it aborts or its top parent commits.
|
||||
@@ -481,9 +498,31 @@ XactLockTableDelete(TransactionId xid)
|
||||
* and if so wait for its parent.
|
||||
*/
|
||||
void
|
||||
XactLockTableWait(TransactionId xid)
|
||||
XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid,
|
||||
XLTW_Oper oper)
|
||||
{
|
||||
LOCKTAG tag;
|
||||
XactLockTableWaitInfo info;
|
||||
ErrorContextCallback callback;
|
||||
|
||||
/*
|
||||
* If an operation is specified, set up our verbose error context
|
||||
* callback.
|
||||
*/
|
||||
if (oper != XLTW_None)
|
||||
{
|
||||
Assert(RelationIsValid(rel));
|
||||
Assert(ItemPointerIsValid(ctid));
|
||||
|
||||
info.rel = rel;
|
||||
info.ctid = ctid;
|
||||
info.oper = oper;
|
||||
|
||||
callback.callback = XactLockTableWaitErrorCb;
|
||||
callback.arg = &info;
|
||||
callback.previous = error_context_stack;
|
||||
error_context_stack = &callback;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@@ -500,6 +539,9 @@ XactLockTableWait(TransactionId xid)
|
||||
break;
|
||||
xid = SubTransGetParent(xid);
|
||||
}
|
||||
|
||||
if (oper != XLTW_None)
|
||||
error_context_stack = callback.previous;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -533,6 +575,62 @@ ConditionalXactLockTableWait(TransactionId xid)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* XactLockTableWaitErrorContextCb
|
||||
* Error context callback for transaction lock waits.
|
||||
*/
|
||||
static void
|
||||
XactLockTableWaitErrorCb(void *arg)
|
||||
{
|
||||
XactLockTableWaitInfo *info = (XactLockTableWaitInfo *) arg;
|
||||
|
||||
/*
|
||||
* We would like to print schema name too, but that would require a
|
||||
* syscache lookup.
|
||||
*/
|
||||
if (info->oper != XLTW_None &&
|
||||
ItemPointerIsValid(info->ctid) && RelationIsValid(info->rel))
|
||||
{
|
||||
const char *cxt;
|
||||
|
||||
switch (info->oper)
|
||||
{
|
||||
case XLTW_Update:
|
||||
cxt = gettext_noop("while updating tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_Delete:
|
||||
cxt = gettext_noop("while deleting tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_Lock:
|
||||
cxt = gettext_noop("while locking tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_LockUpdated:
|
||||
cxt = gettext_noop("while locking updated version (%u,%u) of tuple in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_InsertIndex:
|
||||
cxt = gettext_noop("while inserting index tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_InsertIndexUnique:
|
||||
cxt = gettext_noop("while checking uniqueness of tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_FetchUpdated:
|
||||
cxt = gettext_noop("while rechecking updated tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
case XLTW_RecheckExclusionConstr:
|
||||
cxt = gettext_noop("while checking exclusion constraint on tuple (%u,%u) in relation \"%s\"");
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
errcontext(cxt,
|
||||
ItemPointerGetBlockNumber(info->ctid),
|
||||
ItemPointerGetOffsetNumber(info->ctid),
|
||||
RelationGetRelationName(info->rel));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* WaitForLockersMultiple
|
||||
* Wait until no transaction holds locks that conflict with the given
|
||||
|
Reference in New Issue
Block a user