1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

Add GUC option to log lock acquisition failures.

This commit introduces a new GUC, log_lock_failure, which controls whether
a detailed log message is produced when a lock acquisition fails. Currently,
it only supports logging lock failures caused by SELECT ... NOWAIT.

The log message includes information about all processes holding or
waiting for the lock that couldn't be acquired, helping users analyze and
diagnose the causes of lock failures.

Currently, this option does not log failures from SELECT ... SKIP LOCKED,
as that could generate excessive log messages if many locks are skipped,
causing unnecessary noise.

This mechanism can be extended in the future to support for logging
lock failures from other commands, such as LOCK TABLE ... NOWAIT.

Author: Yuki Seino <seinoyu@oss.nttdata.com>
Co-authored-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Jelte Fennema-Nio <postgres@jeltef.nl>
Discussion: https://postgr.es/m/411280a186cc26ef7034e0f2dfe54131@oss.nttdata.com
This commit is contained in:
Fujii Masao
2025-03-14 23:14:12 +09:00
parent e80171d57c
commit 6d376c3b0d
11 changed files with 211 additions and 86 deletions

View File

@ -7757,6 +7757,26 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-log-lock-failure" xreflabel="log_lock_failure">
<term><varname>log_lock_failure</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>log_lock_failure</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Controls whether a detailed log message is produced
when a lock acquisition fails. This is useful for analyzing
the causes of lock failures. Currently, only lock failures
due to <literal>SELECT NOWAIT</literal> is supported.
The default is <literal>off</literal>. Only superusers and
users with the appropriate <literal>SET</literal> privilege
can change this setting.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-log-recovery-conflict-waits" xreflabel="log_recovery_conflict_waits"> <varlistentry id="guc-log-recovery-conflict-waits" xreflabel="log_recovery_conflict_waits">
<term><varname>log_recovery_conflict_waits</varname> (<type>boolean</type>) <term><varname>log_recovery_conflict_waits</varname> (<type>boolean</type>)
<indexterm> <indexterm>

View File

@ -98,7 +98,8 @@ static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
Relation rel, ItemPointer ctid, XLTW_Oper oper, Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining); int *remaining);
static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining); uint16 infomask, Relation rel, int *remaining,
bool logLockFailure);
static void index_delete_sort(TM_IndexDeleteOp *delstate); static void index_delete_sort(TM_IndexDeleteOp *delstate);
static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate); static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate);
static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup); static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup);
@ -162,8 +163,8 @@ static const struct
LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock) LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
#define UnlockTupleTuplock(rel, tup, mode) \ #define UnlockTupleTuplock(rel, tup, mode) \
UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock) UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock)
#define ConditionalLockTupleTuplock(rel, tup, mode) \ #define ConditionalLockTupleTuplock(rel, tup, mode, log) \
ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock) ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock, (log))
#ifdef USE_PREFETCH #ifdef USE_PREFETCH
/* /*
@ -4894,7 +4895,7 @@ l3:
case LockWaitSkip: case LockWaitSkip:
if (!ConditionalMultiXactIdWait((MultiXactId) xwait, if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
status, infomask, relation, status, infomask, relation,
NULL)) NULL, false))
{ {
result = TM_WouldBlock; result = TM_WouldBlock;
/* recovery code expects to have buffer lock held */ /* recovery code expects to have buffer lock held */
@ -4905,7 +4906,7 @@ l3:
case LockWaitError: case LockWaitError:
if (!ConditionalMultiXactIdWait((MultiXactId) xwait, if (!ConditionalMultiXactIdWait((MultiXactId) xwait,
status, infomask, relation, status, infomask, relation,
NULL)) NULL, log_lock_failure))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE), (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"", errmsg("could not obtain lock on row in relation \"%s\"",
@ -4934,7 +4935,7 @@ l3:
XLTW_Lock); XLTW_Lock);
break; break;
case LockWaitSkip: case LockWaitSkip:
if (!ConditionalXactLockTableWait(xwait)) if (!ConditionalXactLockTableWait(xwait, false))
{ {
result = TM_WouldBlock; result = TM_WouldBlock;
/* recovery code expects to have buffer lock held */ /* recovery code expects to have buffer lock held */
@ -4943,7 +4944,7 @@ l3:
} }
break; break;
case LockWaitError: case LockWaitError:
if (!ConditionalXactLockTableWait(xwait)) if (!ConditionalXactLockTableWait(xwait, log_lock_failure))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE), (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"", errmsg("could not obtain lock on row in relation \"%s\"",
@ -5203,12 +5204,12 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode,
break; break;
case LockWaitSkip: case LockWaitSkip:
if (!ConditionalLockTupleTuplock(relation, tid, mode)) if (!ConditionalLockTupleTuplock(relation, tid, mode, false))
return false; return false;
break; break;
case LockWaitError: case LockWaitError:
if (!ConditionalLockTupleTuplock(relation, tid, mode)) if (!ConditionalLockTupleTuplock(relation, tid, mode, log_lock_failure))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE), (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"", errmsg("could not obtain lock on row in relation \"%s\"",
@ -7581,7 +7582,8 @@ DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask,
* fail if lock is unavailable. 'rel', 'ctid' and 'oper' are used to set up * fail if lock is unavailable. 'rel', 'ctid' and 'oper' are used to set up
* context information for error messages. 'remaining', if not NULL, receives * context information for error messages. 'remaining', if not NULL, receives
* the number of members that are still running, including any (non-aborted) * the number of members that are still running, including any (non-aborted)
* subtransactions of our own transaction. * subtransactions of our own transaction. 'logLockFailure' indicates whether
* to log details when a lock acquisition fails with 'nowait' enabled.
* *
* We do this by sleeping on each member using XactLockTableWait. Any * We do this by sleeping on each member using XactLockTableWait. Any
* members that belong to the current backend are *not* waited for, however; * members that belong to the current backend are *not* waited for, however;
@ -7602,7 +7604,7 @@ static bool
Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, bool nowait, uint16 infomask, bool nowait,
Relation rel, ItemPointer ctid, XLTW_Oper oper, Relation rel, ItemPointer ctid, XLTW_Oper oper,
int *remaining) int *remaining, bool logLockFailure)
{ {
bool result = true; bool result = true;
MultiXactMember *members; MultiXactMember *members;
@ -7649,7 +7651,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
*/ */
if (nowait) if (nowait)
{ {
result = ConditionalXactLockTableWait(memxid); result = ConditionalXactLockTableWait(memxid, logLockFailure);
if (!result) if (!result)
break; break;
} }
@ -7682,7 +7684,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
int *remaining) int *remaining)
{ {
(void) Do_MultiXactIdWait(multi, status, infomask, false, (void) Do_MultiXactIdWait(multi, status, infomask, false,
rel, ctid, oper, remaining); rel, ctid, oper, remaining, false);
} }
/* /*
@ -7700,10 +7702,11 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask,
*/ */
static bool static bool
ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
uint16 infomask, Relation rel, int *remaining) uint16 infomask, Relation rel, int *remaining,
bool logLockFailure)
{ {
return Do_MultiXactIdWait(multi, status, infomask, true, return Do_MultiXactIdWait(multi, status, infomask, true,
rel, NULL, XLTW_None, remaining); rel, NULL, XLTW_None, remaining, logLockFailure);
} }
/* /*

View File

@ -455,12 +455,12 @@ tuple_lock_retry:
XLTW_FetchUpdated); XLTW_FetchUpdated);
break; break;
case LockWaitSkip: case LockWaitSkip:
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax)) if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, false))
/* skip instead of waiting */ /* skip instead of waiting */
return TM_WouldBlock; return TM_WouldBlock;
break; break;
case LockWaitError: case LockWaitError:
if (!ConditionalXactLockTableWait(SnapshotDirty.xmax)) if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, log_lock_failure))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE), (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on row in relation \"%s\"", errmsg("could not obtain lock on row in relation \"%s\"",

View File

@ -112,7 +112,8 @@ LockRelationOid(Oid relid, LOCKMODE lockmode)
SetLocktagRelationOid(&tag, relid); SetLocktagRelationOid(&tag, relid);
res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock,
false);
/* /*
* Now that we have the lock, check for invalidation messages, so that we * Now that we have the lock, check for invalidation messages, so that we
@ -155,7 +156,8 @@ ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
SetLocktagRelationOid(&tag, relid); SetLocktagRelationOid(&tag, relid);
res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock,
false);
if (res == LOCKACQUIRE_NOT_AVAIL) if (res == LOCKACQUIRE_NOT_AVAIL)
return false; return false;
@ -188,7 +190,8 @@ LockRelationId(LockRelId *relid, LOCKMODE lockmode)
SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock,
false);
/* /*
* Now that we have the lock, check for invalidation messages; see notes * Now that we have the lock, check for invalidation messages; see notes
@ -250,7 +253,8 @@ LockRelation(Relation relation, LOCKMODE lockmode)
relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.dbId,
relation->rd_lockInfo.lockRelId.relId); relation->rd_lockInfo.lockRelId.relId);
res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock,
false);
/* /*
* Now that we have the lock, check for invalidation messages; see notes * Now that we have the lock, check for invalidation messages; see notes
@ -281,7 +285,8 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.dbId,
relation->rd_lockInfo.lockRelId.relId); relation->rd_lockInfo.lockRelId.relId);
res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock,
false);
if (res == LOCKACQUIRE_NOT_AVAIL) if (res == LOCKACQUIRE_NOT_AVAIL)
return false; return false;
@ -574,7 +579,8 @@ LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
* Returns true iff the lock was acquired. * Returns true iff the lock was acquired.
*/ */
bool bool
ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode,
bool logLockFailure)
{ {
LOCKTAG tag; LOCKTAG tag;
@ -584,7 +590,8 @@ ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
ItemPointerGetBlockNumber(tid), ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid)); ItemPointerGetOffsetNumber(tid));
return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); return (LockAcquireExtended(&tag, lockmode, false, true, true, NULL,
logLockFailure) != LOCKACQUIRE_NOT_AVAIL);
} }
/* /*
@ -726,7 +733,7 @@ XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid,
* Returns true if the lock was acquired. * Returns true if the lock was acquired.
*/ */
bool bool
ConditionalXactLockTableWait(TransactionId xid) ConditionalXactLockTableWait(TransactionId xid, bool logLockFailure)
{ {
LOCKTAG tag; LOCKTAG tag;
bool first = true; bool first = true;
@ -738,7 +745,9 @@ ConditionalXactLockTableWait(TransactionId xid)
SET_LOCKTAG_TRANSACTION(tag, xid); SET_LOCKTAG_TRANSACTION(tag, xid);
if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL) if (LockAcquireExtended(&tag, ShareLock, false, true, true, NULL,
logLockFailure)
== LOCKACQUIRE_NOT_AVAIL)
return false; return false;
LockRelease(&tag, ShareLock, false); LockRelease(&tag, ShareLock, false);
@ -1027,7 +1036,8 @@ ConditionalLockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
objid, objid,
objsubid); objsubid);
res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock,
false);
if (res == LOCKACQUIRE_NOT_AVAIL) if (res == LOCKACQUIRE_NOT_AVAIL)
return false; return false;
@ -1106,7 +1116,8 @@ ConditionalLockSharedObject(Oid classid, Oid objid, uint16 objsubid,
objid, objid,
objsubid); objsubid);
res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock,
false);
if (res == LOCKACQUIRE_NOT_AVAIL) if (res == LOCKACQUIRE_NOT_AVAIL)
return false; return false;

View File

@ -39,6 +39,7 @@
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pg_trace.h" #include "pg_trace.h"
#include "storage/lmgr.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/spin.h" #include "storage/spin.h"
@ -48,8 +49,9 @@
#include "utils/resowner.h" #include "utils/resowner.h"
/* This configuration variable is used to set the lock table size */ /* GUC variables */
int max_locks_per_xact; /* set by guc.c */ int max_locks_per_xact; /* used to set the lock table size */
bool log_lock_failure = false;
#define NLOCKENTS() \ #define NLOCKENTS() \
mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts)) mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
@ -806,7 +808,7 @@ LockAcquire(const LOCKTAG *locktag,
bool dontWait) bool dontWait)
{ {
return LockAcquireExtended(locktag, lockmode, sessionLock, dontWait, return LockAcquireExtended(locktag, lockmode, sessionLock, dontWait,
true, NULL); true, NULL, false);
} }
/* /*
@ -822,6 +824,9 @@ LockAcquire(const LOCKTAG *locktag,
* *
* If locallockp isn't NULL, *locallockp receives a pointer to the LOCALLOCK * If locallockp isn't NULL, *locallockp receives a pointer to the LOCALLOCK
* table entry if a lock is successfully acquired, or NULL if not. * table entry if a lock is successfully acquired, or NULL if not.
*
* logLockFailure indicates whether to log details when a lock acquisition
* fails with dontWait = true.
*/ */
LockAcquireResult LockAcquireResult
LockAcquireExtended(const LOCKTAG *locktag, LockAcquireExtended(const LOCKTAG *locktag,
@ -829,7 +834,8 @@ LockAcquireExtended(const LOCKTAG *locktag,
bool sessionLock, bool sessionLock,
bool dontWait, bool dontWait,
bool reportMemoryError, bool reportMemoryError,
LOCALLOCK **locallockp) LOCALLOCK **locallockp,
bool logLockFailure)
{ {
LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid; LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid;
LockMethod lockMethodTable; LockMethod lockMethodTable;
@ -1145,6 +1151,47 @@ LockAcquireExtended(const LOCKTAG *locktag,
if (dontWait) if (dontWait)
{ {
/*
* Log lock holders and waiters as a detail log message if
* logLockFailure = true and lock acquisition fails with dontWait
* = true
*/
if (logLockFailure)
{
StringInfoData buf,
lock_waiters_sbuf,
lock_holders_sbuf;
const char *modename;
int lockHoldersNum = 0;
initStringInfo(&buf);
initStringInfo(&lock_waiters_sbuf);
initStringInfo(&lock_holders_sbuf);
DescribeLockTag(&buf, &locallock->tag.lock);
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
lockmode);
/* Gather a list of all lock holders and waiters */
LWLockAcquire(partitionLock, LW_SHARED);
GetLockHoldersAndWaiters(locallock, &lock_holders_sbuf,
&lock_waiters_sbuf, &lockHoldersNum);
LWLockRelease(partitionLock);
ereport(LOG,
(errmsg("process %d could not obtain %s on %s",
MyProcPid, modename, buf.data),
errdetail_log_plural(
"Process holding the lock: %s, Wait queue: %s.",
"Processes holding the lock: %s, Wait queue: %s.",
lockHoldersNum,
lock_holders_sbuf.data,
lock_waiters_sbuf.data)));
pfree(buf.data);
pfree(lock_holders_sbuf.data);
pfree(lock_waiters_sbuf.data);
}
if (locallockp) if (locallockp)
*locallockp = NULL; *locallockp = NULL;
return LOCKACQUIRE_NOT_AVAIL; return LOCKACQUIRE_NOT_AVAIL;

View File

@ -1514,10 +1514,6 @@ ProcSleep(LOCALLOCK *locallock)
long secs; long secs;
int usecs; int usecs;
long msecs; long msecs;
dlist_iter proc_iter;
PROCLOCK *curproclock;
bool first_holder = true,
first_waiter = true;
int lockHoldersNum = 0; int lockHoldersNum = 0;
initStringInfo(&buf); initStringInfo(&buf);
@ -1533,54 +1529,10 @@ ProcSleep(LOCALLOCK *locallock)
msecs = secs * 1000 + usecs / 1000; msecs = secs * 1000 + usecs / 1000;
usecs = usecs % 1000; usecs = usecs % 1000;
/* /* Gather a list of all lock holders and waiters */
* we loop over the lock's procLocks to gather a list of all
* holders and waiters. Thus we will be able to provide more
* detailed information for lock debugging purposes.
*
* lock->procLocks contains all processes which hold or wait for
* this lock.
*/
LWLockAcquire(partitionLock, LW_SHARED); LWLockAcquire(partitionLock, LW_SHARED);
GetLockHoldersAndWaiters(locallock, &lock_holders_sbuf,
dlist_foreach(proc_iter, &lock->procLocks) &lock_waiters_sbuf, &lockHoldersNum);
{
curproclock =
dlist_container(PROCLOCK, lockLink, proc_iter.cur);
/*
* we are a waiter if myProc->waitProcLock == curproclock; we
* are a holder if it is NULL or something different
*/
if (curproclock->tag.myProc->waitProcLock == curproclock)
{
if (first_waiter)
{
appendStringInfo(&lock_waiters_sbuf, "%d",
curproclock->tag.myProc->pid);
first_waiter = false;
}
else
appendStringInfo(&lock_waiters_sbuf, ", %d",
curproclock->tag.myProc->pid);
}
else
{
if (first_holder)
{
appendStringInfo(&lock_holders_sbuf, "%d",
curproclock->tag.myProc->pid);
first_holder = false;
}
else
appendStringInfo(&lock_holders_sbuf, ", %d",
curproclock->tag.myProc->pid);
lockHoldersNum++;
}
}
LWLockRelease(partitionLock); LWLockRelease(partitionLock);
if (deadlock_state == DS_SOFT_DEADLOCK) if (deadlock_state == DS_SOFT_DEADLOCK)
@ -1885,6 +1837,81 @@ CheckDeadLockAlert(void)
errno = save_errno; errno = save_errno;
} }
/*
* GetLockHoldersAndWaiters - get lock holders and waiters for a lock
*
* Fill lock_holders_sbuf and lock_waiters_sbuf with the PIDs of processes holding
* and waiting for the lock, and set lockHoldersNum to the number of lock holders.
*
* The lock table's partition lock must be held on entry and remains held on exit.
*/
void
GetLockHoldersAndWaiters(LOCALLOCK *locallock, StringInfo lock_holders_sbuf,
StringInfo lock_waiters_sbuf, int *lockHoldersNum)
{
dlist_iter proc_iter;
PROCLOCK *curproclock;
LOCK *lock = locallock->lock;
bool first_holder = true,
first_waiter = true;
#ifdef USE_ASSERT_CHECKING
{
uint32 hashcode = locallock->hashcode;
LWLock *partitionLock = LockHashPartitionLock(hashcode);
Assert(LWLockHeldByMe(partitionLock));
}
#endif
*lockHoldersNum = 0;
/*
* Loop over the lock's procLocks to gather a list of all holders and
* waiters. Thus we will be able to provide more detailed information for
* lock debugging purposes.
*
* lock->procLocks contains all processes which hold or wait for this
* lock.
*/
dlist_foreach(proc_iter, &lock->procLocks)
{
curproclock =
dlist_container(PROCLOCK, lockLink, proc_iter.cur);
/*
* We are a waiter if myProc->waitProcLock == curproclock; we are a
* holder if it is NULL or something different.
*/
if (curproclock->tag.myProc->waitProcLock == curproclock)
{
if (first_waiter)
{
appendStringInfo(lock_waiters_sbuf, "%d",
curproclock->tag.myProc->pid);
first_waiter = false;
}
else
appendStringInfo(lock_waiters_sbuf, ", %d",
curproclock->tag.myProc->pid);
}
else
{
if (first_holder)
{
appendStringInfo(lock_holders_sbuf, "%d",
curproclock->tag.myProc->pid);
first_holder = false;
}
else
appendStringInfo(lock_holders_sbuf, ", %d",
curproclock->tag.myProc->pid);
(*lockHoldersNum)++;
}
}
}
/* /*
* ProcWaitForSignal - wait for a signal from another backend. * ProcWaitForSignal - wait for a signal from another backend.
* *

View File

@ -1585,6 +1585,15 @@ struct config_bool ConfigureNamesBool[] =
false, false,
NULL, NULL, NULL NULL, NULL, NULL
}, },
{
{"log_lock_failure", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs lock failures."),
NULL
},
&log_lock_failure,
false,
NULL, NULL, NULL
},
{ {
{"log_recovery_conflict_waits", PGC_SIGHUP, LOGGING_WHAT, {"log_recovery_conflict_waits", PGC_SIGHUP, LOGGING_WHAT,
gettext_noop("Logs standby recovery conflict waits."), gettext_noop("Logs standby recovery conflict waits."),

View File

@ -610,6 +610,7 @@
# %% = '%' # %% = '%'
# e.g. '<%u%%%d> ' # e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout #log_lock_waits = off # log lock waits >= deadlock_timeout
#log_lock_failure = off # log lock failures
#log_recovery_conflict_waits = off # log standby recovery conflict waits #log_recovery_conflict_waits = off # log standby recovery conflict waits
# >= deadlock_timeout # >= deadlock_timeout
#log_parameter_max_length = -1 # when logging statements, limit logged #log_parameter_max_length = -1 # when logging statements, limit logged

View File

@ -73,7 +73,7 @@ extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
/* Lock a tuple (see heap_lock_tuple before assuming you understand this) */ /* Lock a tuple (see heap_lock_tuple before assuming you understand this) */
extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode); extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
extern bool ConditionalLockTuple(Relation relation, ItemPointer tid, extern bool ConditionalLockTuple(Relation relation, ItemPointer tid,
LOCKMODE lockmode); LOCKMODE lockmode, bool logLockFailure);
extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode); extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
/* Lock an XID (used to wait for a transaction to finish) */ /* Lock an XID (used to wait for a transaction to finish) */
@ -81,7 +81,8 @@ extern void XactLockTableInsert(TransactionId xid);
extern void XactLockTableDelete(TransactionId xid); extern void XactLockTableDelete(TransactionId xid);
extern void XactLockTableWait(TransactionId xid, Relation rel, extern void XactLockTableWait(TransactionId xid, Relation rel,
ItemPointer ctid, XLTW_Oper oper); ItemPointer ctid, XLTW_Oper oper);
extern bool ConditionalXactLockTableWait(TransactionId xid); extern bool ConditionalXactLockTableWait(TransactionId xid,
bool logLockFailure);
/* Lock VXIDs, specified by conflicting locktags */ /* Lock VXIDs, specified by conflicting locktags */
extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode, bool progress); extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode, bool progress);

View File

@ -30,6 +30,7 @@ typedef struct PGPROC PGPROC;
/* GUC variables */ /* GUC variables */
extern PGDLLIMPORT int max_locks_per_xact; extern PGDLLIMPORT int max_locks_per_xact;
extern PGDLLIMPORT bool log_lock_failure;
#ifdef LOCK_DEBUG #ifdef LOCK_DEBUG
extern PGDLLIMPORT int Trace_lock_oidmin; extern PGDLLIMPORT int Trace_lock_oidmin;
@ -560,7 +561,8 @@ extern LockAcquireResult LockAcquireExtended(const LOCKTAG *locktag,
bool sessionLock, bool sessionLock,
bool dontWait, bool dontWait,
bool reportMemoryError, bool reportMemoryError,
LOCALLOCK **locallockp); LOCALLOCK **locallockp,
bool logLockFailure);
extern void AbortStrongLockAcquire(void); extern void AbortStrongLockAcquire(void);
extern void MarkLockClear(LOCALLOCK *locallock); extern void MarkLockClear(LOCALLOCK *locallock);
extern bool LockRelease(const LOCKTAG *locktag, extern bool LockRelease(const LOCKTAG *locktag,

View File

@ -489,6 +489,10 @@ extern void ProcWakeup(PGPROC *proc, ProcWaitStatus waitStatus);
extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock); extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
extern void CheckDeadLockAlert(void); extern void CheckDeadLockAlert(void);
extern void LockErrorCleanup(void); extern void LockErrorCleanup(void);
extern void GetLockHoldersAndWaiters(LOCALLOCK *locallock,
StringInfo lock_holders_sbuf,
StringInfo lock_waiters_sbuf,
int *lockHoldersNum);
extern void ProcWaitForSignal(uint32 wait_event_info); extern void ProcWaitForSignal(uint32 wait_event_info);
extern void ProcSendSignal(ProcNumber procNumber); extern void ProcSendSignal(ProcNumber procNumber);