mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Show PIDs of lock holders and waiters in log_lock_waits log message.
Christian Kruse, reviewed by Kumar Rajeev Rastogi.
This commit is contained in:
@ -249,6 +249,15 @@ ereport(ERROR,
|
|||||||
sent to the client.
|
sent to the client.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<function>errdetail_log_plural(const char *fmt_singuar, const char
|
||||||
|
*fmt_plural, unsigned long n, ...)</function> is like
|
||||||
|
<function>errdetail_log</>, but with support for various plural forms of
|
||||||
|
the message.
|
||||||
|
For more information see <xref linkend="nls-guidelines">.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<function>errhint(const char *msg, ...)</function> supplies an optional
|
<function>errhint(const char *msg, ...)</function> supplies an optional
|
||||||
|
@ -1208,13 +1208,23 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
*/
|
*/
|
||||||
if (log_lock_waits && deadlock_state != DS_NOT_YET_CHECKED)
|
if (log_lock_waits && deadlock_state != DS_NOT_YET_CHECKED)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
StringInfoData buf,
|
||||||
|
lock_waiters_sbuf,
|
||||||
|
lock_holders_sbuf;
|
||||||
const char *modename;
|
const char *modename;
|
||||||
long secs;
|
long secs;
|
||||||
int usecs;
|
int usecs;
|
||||||
long msecs;
|
long msecs;
|
||||||
|
SHM_QUEUE *procLocks;
|
||||||
|
PROCLOCK *proclock;
|
||||||
|
bool first_holder = true,
|
||||||
|
first_waiter = true;
|
||||||
|
int lockHoldersNum = 0;
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
|
initStringInfo(&lock_waiters_sbuf);
|
||||||
|
initStringInfo(&lock_holders_sbuf);
|
||||||
|
|
||||||
DescribeLockTag(&buf, &locallock->tag.lock);
|
DescribeLockTag(&buf, &locallock->tag.lock);
|
||||||
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
|
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
|
||||||
lockmode);
|
lockmode);
|
||||||
@ -1224,10 +1234,67 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
msecs = secs * 1000 + usecs / 1000;
|
msecs = secs * 1000 + usecs / 1000;
|
||||||
usecs = usecs % 1000;
|
usecs = usecs % 1000;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
procLocks = &(lock->procLocks);
|
||||||
|
proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks,
|
||||||
|
offsetof(PROCLOCK, lockLink));
|
||||||
|
|
||||||
|
while (proclock)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* we are a waiter if myProc->waitProcLock == proclock; we are
|
||||||
|
* a holder if it is NULL or something different
|
||||||
|
*/
|
||||||
|
if (proclock->tag.myProc->waitProcLock == proclock)
|
||||||
|
{
|
||||||
|
if (first_waiter)
|
||||||
|
{
|
||||||
|
appendStringInfo(&lock_waiters_sbuf, "%d",
|
||||||
|
proclock->tag.myProc->pid);
|
||||||
|
first_waiter = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendStringInfo(&lock_waiters_sbuf, ", %d",
|
||||||
|
proclock->tag.myProc->pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (first_holder)
|
||||||
|
{
|
||||||
|
appendStringInfo(&lock_holders_sbuf, "%d",
|
||||||
|
proclock->tag.myProc->pid);
|
||||||
|
first_holder = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendStringInfo(&lock_holders_sbuf, ", %d",
|
||||||
|
proclock->tag.myProc->pid);
|
||||||
|
|
||||||
|
lockHoldersNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
proclock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->lockLink,
|
||||||
|
offsetof(PROCLOCK, lockLink));
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockRelease(partitionLock);
|
||||||
|
|
||||||
if (deadlock_state == DS_SOFT_DEADLOCK)
|
if (deadlock_state == DS_SOFT_DEADLOCK)
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("process %d avoided deadlock for %s on %s by rearranging queue order after %ld.%03d ms",
|
(errmsg("process %d avoided deadlock for %s on %s by rearranging queue order after %ld.%03d ms",
|
||||||
MyProcPid, modename, buf.data, msecs, usecs)));
|
MyProcPid, modename, buf.data, msecs, usecs),
|
||||||
|
(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))));
|
||||||
else if (deadlock_state == DS_HARD_DEADLOCK)
|
else if (deadlock_state == DS_HARD_DEADLOCK)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1239,13 +1306,19 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
*/
|
*/
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("process %d detected deadlock while waiting for %s on %s after %ld.%03d ms",
|
(errmsg("process %d detected deadlock while waiting for %s on %s after %ld.%03d ms",
|
||||||
MyProcPid, modename, buf.data, msecs, usecs)));
|
MyProcPid, modename, buf.data, msecs, usecs),
|
||||||
|
(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))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myWaitStatus == STATUS_WAITING)
|
if (myWaitStatus == STATUS_WAITING)
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("process %d still waiting for %s on %s after %ld.%03d ms",
|
(errmsg("process %d still waiting for %s on %s after %ld.%03d ms",
|
||||||
MyProcPid, modename, buf.data, msecs, usecs)));
|
MyProcPid, modename, buf.data, msecs, usecs),
|
||||||
|
(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))));
|
||||||
else if (myWaitStatus == STATUS_OK)
|
else if (myWaitStatus == STATUS_OK)
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("process %d acquired %s on %s after %ld.%03d ms",
|
(errmsg("process %d acquired %s on %s after %ld.%03d ms",
|
||||||
@ -1265,7 +1338,10 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
if (deadlock_state != DS_HARD_DEADLOCK)
|
if (deadlock_state != DS_HARD_DEADLOCK)
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("process %d failed to acquire %s on %s after %ld.%03d ms",
|
(errmsg("process %d failed to acquire %s on %s after %ld.%03d ms",
|
||||||
MyProcPid, modename, buf.data, msecs, usecs)));
|
MyProcPid, modename, buf.data, msecs, usecs),
|
||||||
|
(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))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1275,6 +1351,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
deadlock_state = DS_NO_DEADLOCK;
|
deadlock_state = DS_NO_DEADLOCK;
|
||||||
|
|
||||||
pfree(buf.data);
|
pfree(buf.data);
|
||||||
|
pfree(lock_holders_sbuf.data);
|
||||||
|
pfree(lock_waiters_sbuf.data);
|
||||||
}
|
}
|
||||||
} while (myWaitStatus == STATUS_WAITING);
|
} while (myWaitStatus == STATUS_WAITING);
|
||||||
|
|
||||||
|
@ -937,6 +937,28 @@ errdetail_log(const char *fmt,...)
|
|||||||
return 0; /* return value does not matter */
|
return 0; /* return value does not matter */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* errdetail_log_plural --- add a detail_log error message text to the current error
|
||||||
|
* with support for pluralization of the message text
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
errdetail_log_plural(const char *fmt_singular, const char *fmt_plural,
|
||||||
|
unsigned long n,...)
|
||||||
|
{
|
||||||
|
ErrorData *edata = &errordata[errordata_stack_depth];
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
|
recursion_depth++;
|
||||||
|
CHECK_STACK_DEPTH();
|
||||||
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
|
EVALUATE_MESSAGE_PLURAL(edata->domain, detail_log, false);
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
recursion_depth--;
|
||||||
|
return 0; /* return value does not matter */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* errdetail_plural --- add a detail error message text to the current error,
|
* errdetail_plural --- add a detail error message text to the current error,
|
||||||
|
@ -181,6 +181,14 @@ errdetail_log(const char *fmt,...)
|
|||||||
the supplied arguments. */
|
the supplied arguments. */
|
||||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
|
||||||
|
|
||||||
|
extern int
|
||||||
|
errdetail_log_plural(const char *fmt_singular, const char *fmt_plural,
|
||||||
|
unsigned long n,...)
|
||||||
|
/* This extension allows gcc to check the format string for consistency with
|
||||||
|
the supplied arguments. */
|
||||||
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 4)))
|
||||||
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 4)));
|
||||||
|
|
||||||
extern int
|
extern int
|
||||||
errdetail_plural(const char *fmt_singular, const char *fmt_plural,
|
errdetail_plural(const char *fmt_singular, const char *fmt_plural,
|
||||||
unsigned long n,...)
|
unsigned long n,...)
|
||||||
|
Reference in New Issue
Block a user