Code review for log_lock_waits patch. Don't try to issue log messages from
within a signal handler (this might be safe given the relatively narrow code range in which the interrupt is enabled, but it seems awfully risky); do issue more informative log messages that tell what is being waited for and the exact length of the wait; minor other code cleanup. Greg Stark and Tom Lane
This commit is contained in:
parent
4c310eca2e
commit
6e07228728
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.126 2007/06/07 19:19:56 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.127 2007/06/19 20:13:21 tgl Exp $ -->
|
||||||
|
|
||||||
<chapter Id="runtime-config">
|
<chapter Id="runtime-config">
|
||||||
<title>Server Configuration</title>
|
<title>Server Configuration</title>
|
||||||
@ -2947,7 +2947,7 @@ SELECT * FROM parent WHERE key = 2400;
|
|||||||
</indexterm>
|
</indexterm>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Controls whether a log message is produced when a statement waits
|
Controls whether a log message is produced when a session waits
|
||||||
longer than <xref linkend="guc-deadlock-timeout"> to acquire a
|
longer than <xref linkend="guc-deadlock-timeout"> to acquire a
|
||||||
lock. This is useful in determining if lock waits are causing
|
lock. This is useful in determining if lock waits are causing
|
||||||
poor performance. The default is <literal>off</>.
|
poor performance. The default is <literal>off</>.
|
||||||
@ -4084,11 +4084,18 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
|
|||||||
wasted in needless deadlock checks, but slows down reporting of
|
wasted in needless deadlock checks, but slows down reporting of
|
||||||
real deadlock errors. The default is one second (<literal>1s</>),
|
real deadlock errors. The default is one second (<literal>1s</>),
|
||||||
which is probably about the smallest value you would want in
|
which is probably about the smallest value you would want in
|
||||||
practice. Set <xref linkend="guc-log-lock-waits"> to log deadlock
|
practice. On a heavily loaded server you might want to raise it.
|
||||||
checks. On a heavily loaded server you might want to raise it.
|
|
||||||
Ideally the setting should exceed your typical transaction time,
|
Ideally the setting should exceed your typical transaction time,
|
||||||
so as to improve the odds that a lock will be released before the
|
so as to improve the odds that a lock will be released before
|
||||||
waiter decides to check for deadlock.
|
the waiter decides to check for deadlock.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When <xref linkend="guc-log-lock-waits"> is set,
|
||||||
|
this parameter also determines the length of time to wait before
|
||||||
|
a log message is issued about the lock wait. If you are trying
|
||||||
|
to investigate locking delays you might want to set a shorter than
|
||||||
|
normal <varname>deadlock_timeout</varname>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.47 2007/04/20 20:15:52 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.48 2007/06/19 20:13:21 tgl Exp $
|
||||||
*
|
*
|
||||||
* Interface:
|
* Interface:
|
||||||
*
|
*
|
||||||
@ -25,8 +25,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "lib/stringinfo.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "storage/lmgr.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
@ -117,8 +117,9 @@ static int nDeadlockDetails;
|
|||||||
* allocation of working memory for DeadLockCheck. We do this per-backend
|
* allocation of working memory for DeadLockCheck. We do this per-backend
|
||||||
* since there's no percentage in making the kernel do copy-on-write
|
* since there's no percentage in making the kernel do copy-on-write
|
||||||
* inheritance of workspace from the postmaster. We want to allocate the
|
* inheritance of workspace from the postmaster. We want to allocate the
|
||||||
* space at startup because the deadlock checker might be invoked when there's
|
* space at startup because (a) the deadlock checker might be invoked when
|
||||||
* no free memory left.
|
* there's no free memory left, and (b) the checker is normally run inside a
|
||||||
|
* signal handler, which is a very dangerous place to invoke palloc from.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
InitDeadLockChecking(void)
|
InitDeadLockChecking(void)
|
||||||
@ -184,17 +185,17 @@ InitDeadLockChecking(void)
|
|||||||
*
|
*
|
||||||
* This code looks for deadlocks involving the given process. If any
|
* This code looks for deadlocks involving the given process. If any
|
||||||
* are found, it tries to rearrange lock wait queues to resolve the
|
* are found, it tries to rearrange lock wait queues to resolve the
|
||||||
* deadlock. If resolution is impossible, return TRUE --- the caller
|
* deadlock. If resolution is impossible, return DS_HARD_DEADLOCK ---
|
||||||
* is then expected to abort the given proc's transaction.
|
* the caller is then expected to abort the given proc's transaction.
|
||||||
*
|
*
|
||||||
* Caller must already have locked all partitions of the lock tables,
|
* Caller must already have locked all partitions of the lock tables.
|
||||||
* so standard error logging/reporting code is handled by caller.
|
|
||||||
*
|
*
|
||||||
* On failure, deadlock details are recorded in deadlockDetails[] for
|
* On failure, deadlock details are recorded in deadlockDetails[] for
|
||||||
* subsequent printing by DeadLockReport(). That activity is separate
|
* subsequent printing by DeadLockReport(). That activity is separate
|
||||||
* because we don't want to do it while holding all those LWLocks.
|
* because (a) we don't want to do it while holding all those LWLocks,
|
||||||
|
* and (b) we are typically invoked inside a signal handler.
|
||||||
*/
|
*/
|
||||||
DeadlockState
|
DeadLockState
|
||||||
DeadLockCheck(PGPROC *proc)
|
DeadLockCheck(PGPROC *proc)
|
||||||
{
|
{
|
||||||
int i,
|
int i,
|
||||||
@ -205,11 +206,6 @@ DeadLockCheck(PGPROC *proc)
|
|||||||
nPossibleConstraints = 0;
|
nPossibleConstraints = 0;
|
||||||
nWaitOrders = 0;
|
nWaitOrders = 0;
|
||||||
|
|
||||||
#ifdef LOCK_DEBUG
|
|
||||||
if (Debug_deadlocks)
|
|
||||||
DumpAllLocks();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Search for deadlocks and possible fixes */
|
/* Search for deadlocks and possible fixes */
|
||||||
if (DeadLockCheckRecurse(proc))
|
if (DeadLockCheckRecurse(proc))
|
||||||
{
|
{
|
||||||
@ -256,10 +252,11 @@ DeadLockCheck(PGPROC *proc)
|
|||||||
ProcLockWakeup(GetLocksMethodTable(lock), lock);
|
ProcLockWakeup(GetLocksMethodTable(lock), lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return code tells caller if we had to escape a deadlock or not */
|
||||||
if (nWaitOrders > 0)
|
if (nWaitOrders > 0)
|
||||||
return DS_SOFT_DEADLOCK;
|
return DS_SOFT_DEADLOCK;
|
||||||
else
|
else
|
||||||
return DS_DEADLOCK_NOT_FOUND;
|
return DS_NO_DEADLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -833,82 +830,7 @@ PrintLockQueue(LOCK *lock, const char *info)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Append a description of a lockable object to buf.
|
* Report a detected deadlock, with available details.
|
||||||
*
|
|
||||||
* XXX probably this should be exported from lmgr.c or some such place.
|
|
||||||
* Ideally we would print names for the numeric values, but that requires
|
|
||||||
* getting locks on system tables, which might cause problems.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
DescribeLockTag(StringInfo buf, const LOCKTAG *lock)
|
|
||||||
{
|
|
||||||
switch (lock->locktag_type)
|
|
||||||
{
|
|
||||||
case LOCKTAG_RELATION:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("relation %u of database %u"),
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field1);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_RELATION_EXTEND:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("extension of relation %u of database %u"),
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field1);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_PAGE:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("page %u of relation %u of database %u"),
|
|
||||||
lock->locktag_field3,
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field1);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_TUPLE:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("tuple (%u,%u) of relation %u of database %u"),
|
|
||||||
lock->locktag_field3,
|
|
||||||
lock->locktag_field4,
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field1);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_TRANSACTION:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("transaction %u"),
|
|
||||||
lock->locktag_field1);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_OBJECT:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("object %u of class %u of database %u"),
|
|
||||||
lock->locktag_field3,
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field1);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_USERLOCK:
|
|
||||||
/* reserved for old contrib code, now on pgfoundry */
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("user lock [%u,%u,%u]"),
|
|
||||||
lock->locktag_field1,
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field3);
|
|
||||||
break;
|
|
||||||
case LOCKTAG_ADVISORY:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("advisory lock [%u,%u,%u,%u]"),
|
|
||||||
lock->locktag_field1,
|
|
||||||
lock->locktag_field2,
|
|
||||||
lock->locktag_field3,
|
|
||||||
lock->locktag_field4);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
appendStringInfo(buf,
|
|
||||||
_("unrecognized locktag type %d"),
|
|
||||||
lock->locktag_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Report a detected DS_HARD_DEADLOCK, with available details.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DeadLockReport(void)
|
DeadLockReport(void)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.90 2007/01/05 22:19:38 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.91 2007/06/19 20:13:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -635,3 +635,79 @@ LockTagIsTemp(const LOCKTAG *tag)
|
|||||||
}
|
}
|
||||||
return false; /* default case */
|
return false; /* default case */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a description of a lockable object to buf.
|
||||||
|
*
|
||||||
|
* Ideally we would print names for the numeric values, but that requires
|
||||||
|
* getting locks on system tables, which might cause problems since this is
|
||||||
|
* typically used to report deadlock situations.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
|
||||||
|
{
|
||||||
|
switch (tag->locktag_type)
|
||||||
|
{
|
||||||
|
case LOCKTAG_RELATION:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("relation %u of database %u"),
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field1);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_RELATION_EXTEND:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("extension of relation %u of database %u"),
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field1);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_PAGE:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("page %u of relation %u of database %u"),
|
||||||
|
tag->locktag_field3,
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field1);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_TUPLE:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("tuple (%u,%u) of relation %u of database %u"),
|
||||||
|
tag->locktag_field3,
|
||||||
|
tag->locktag_field4,
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field1);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_TRANSACTION:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("transaction %u"),
|
||||||
|
tag->locktag_field1);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_OBJECT:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("object %u of class %u of database %u"),
|
||||||
|
tag->locktag_field3,
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field1);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_USERLOCK:
|
||||||
|
/* reserved for old contrib code, now on pgfoundry */
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("user lock [%u,%u,%u]"),
|
||||||
|
tag->locktag_field1,
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field3);
|
||||||
|
break;
|
||||||
|
case LOCKTAG_ADVISORY:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("advisory lock [%u,%u,%u,%u]"),
|
||||||
|
tag->locktag_field1,
|
||||||
|
tag->locktag_field2,
|
||||||
|
tag->locktag_field3,
|
||||||
|
tag->locktag_field4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
appendStringInfo(buf,
|
||||||
|
_("unrecognized locktag type %d"),
|
||||||
|
tag->locktag_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.188 2007/04/16 18:29:53 alvherre Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.189 2007/06/19 20:13:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -40,6 +40,7 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "postmaster/autovacuum.h"
|
#include "postmaster/autovacuum.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.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"
|
||||||
@ -72,8 +73,12 @@ static LOCALLOCK *lockAwaited = NULL;
|
|||||||
/* Mark these volatile because they can be changed by signal handler */
|
/* Mark these volatile because they can be changed by signal handler */
|
||||||
static volatile bool statement_timeout_active = false;
|
static volatile bool statement_timeout_active = false;
|
||||||
static volatile bool deadlock_timeout_active = false;
|
static volatile bool deadlock_timeout_active = false;
|
||||||
|
static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED;
|
||||||
volatile bool cancel_from_timeout = false;
|
volatile bool cancel_from_timeout = false;
|
||||||
|
|
||||||
|
/* timeout_start_time is set when log_lock_waits is true */
|
||||||
|
static TimestampTz timeout_start_time;
|
||||||
|
|
||||||
/* statement_fin_time is valid only if statement_timeout_active is true */
|
/* statement_fin_time is valid only if statement_timeout_active is true */
|
||||||
static TimestampTz statement_fin_time;
|
static TimestampTz statement_fin_time;
|
||||||
|
|
||||||
@ -837,6 +842,9 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
*/
|
*/
|
||||||
LWLockRelease(partitionLock);
|
LWLockRelease(partitionLock);
|
||||||
|
|
||||||
|
/* Reset deadlock_state before enabling the signal handler */
|
||||||
|
deadlock_state = DS_NOT_YET_CHECKED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set timer so we can wake up after awhile and check for a deadlock. If a
|
* Set timer so we can wake up after awhile and check for a deadlock. If a
|
||||||
* deadlock is detected, the handler releases the process's semaphore and
|
* deadlock is detected, the handler releases the process's semaphore and
|
||||||
@ -869,6 +877,60 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
PGSemaphoreLock(&MyProc->sem, true);
|
PGSemaphoreLock(&MyProc->sem, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If awoken after the deadlock check interrupt has run, and
|
||||||
|
* log_lock_waits is on, then report about the wait.
|
||||||
|
*/
|
||||||
|
if (log_lock_waits)
|
||||||
|
{
|
||||||
|
switch (deadlock_state)
|
||||||
|
{
|
||||||
|
case DS_NOT_YET_CHECKED:
|
||||||
|
/* Spurious wakeup as described above */
|
||||||
|
break;
|
||||||
|
case DS_NO_DEADLOCK:
|
||||||
|
case DS_SOFT_DEADLOCK:
|
||||||
|
{
|
||||||
|
StringInfoData buf;
|
||||||
|
const char *modename;
|
||||||
|
long secs;
|
||||||
|
int usecs;
|
||||||
|
long msecs;
|
||||||
|
|
||||||
|
initStringInfo(&buf);
|
||||||
|
DescribeLockTag(&buf, &locallock->tag.lock);
|
||||||
|
modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
|
||||||
|
lockmode);
|
||||||
|
TimestampDifference(timeout_start_time,
|
||||||
|
GetCurrentTimestamp(),
|
||||||
|
&secs, &usecs);
|
||||||
|
msecs = secs * 1000 + usecs / 1000;
|
||||||
|
usecs = usecs % 1000;
|
||||||
|
|
||||||
|
if (deadlock_state == DS_SOFT_DEADLOCK)
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("deadlock for %s on %s avoided by rearranging queue order after %ld.%03d ms",
|
||||||
|
modename, buf.data,
|
||||||
|
msecs, usecs)));
|
||||||
|
else if (MyProc->waitStatus == STATUS_WAITING)
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("process %d still waiting for %s on %s after %ld.%03d ms",
|
||||||
|
MyProcPid, modename, buf.data,
|
||||||
|
msecs, usecs)));
|
||||||
|
else
|
||||||
|
ereport(LOG,
|
||||||
|
(errmsg("process %d acquired %s on %s after %ld.%03d ms",
|
||||||
|
MyProcPid, modename, buf.data,
|
||||||
|
msecs, usecs)));
|
||||||
|
pfree(buf.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DS_HARD_DEADLOCK:
|
||||||
|
/* ERROR will be reported below, so no message here */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} while (MyProc->waitStatus == STATUS_WAITING);
|
} while (MyProc->waitStatus == STATUS_WAITING);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1011,14 +1073,17 @@ ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock)
|
|||||||
* We only get to this routine if we got SIGALRM after DeadlockTimeout
|
* We only get to this routine if we got SIGALRM after DeadlockTimeout
|
||||||
* while waiting for a lock to be released by some other process. Look
|
* while waiting for a lock to be released by some other process. Look
|
||||||
* to see if there's a deadlock; if not, just return and continue waiting.
|
* to see if there's a deadlock; if not, just return and continue waiting.
|
||||||
|
* (But signal ProcSleep to log a message, if log_lock_waits is true.)
|
||||||
* If we have a real deadlock, remove ourselves from the lock's wait queue
|
* If we have a real deadlock, remove ourselves from the lock's wait queue
|
||||||
* and signal an error to ProcSleep.
|
* and signal an error to ProcSleep.
|
||||||
|
*
|
||||||
|
* NB: this is run inside a signal handler, so be very wary about what is done
|
||||||
|
* here or in called routines.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CheckDeadLock(void)
|
CheckDeadLock(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
DeadlockState deadlock_state = DS_DEADLOCK_NOT_FOUND;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Acquire exclusive lock on the entire shared lock data structures. Must
|
* Acquire exclusive lock on the entire shared lock data structures. Must
|
||||||
@ -1044,22 +1109,31 @@ CheckDeadLock(void)
|
|||||||
* This is quicker than checking our semaphore's state, since no kernel
|
* This is quicker than checking our semaphore's state, since no kernel
|
||||||
* call is needed, and it is safe because we hold the lock partition lock.
|
* call is needed, and it is safe because we hold the lock partition lock.
|
||||||
*/
|
*/
|
||||||
if (MyProc->links.prev != INVALID_OFFSET &&
|
if (MyProc->links.prev == INVALID_OFFSET ||
|
||||||
MyProc->links.next != INVALID_OFFSET)
|
MyProc->links.next == INVALID_OFFSET)
|
||||||
deadlock_state = DeadLockCheck(MyProc);
|
goto check_done;
|
||||||
|
|
||||||
|
#ifdef LOCK_DEBUG
|
||||||
|
if (Debug_deadlocks)
|
||||||
|
DumpAllLocks();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Run the deadlock check, and set deadlock_state for use by ProcSleep */
|
||||||
|
deadlock_state = DeadLockCheck(MyProc);
|
||||||
|
|
||||||
if (deadlock_state == DS_HARD_DEADLOCK)
|
if (deadlock_state == DS_HARD_DEADLOCK)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Oops. We have a deadlock.
|
* Oops. We have a deadlock.
|
||||||
*
|
*
|
||||||
* Get this process out of wait state. (Note: we could do this more
|
* Get this process out of wait state. (Note: we could do this more
|
||||||
* efficiently by relying on lockAwaited, but use this coding to preserve
|
* efficiently by relying on lockAwaited, but use this coding to
|
||||||
* the flexibility to kill some other transaction than the one detecting
|
* preserve the flexibility to kill some other transaction than the
|
||||||
* the deadlock.)
|
* one detecting the deadlock.)
|
||||||
*
|
*
|
||||||
* RemoveFromWaitQueue sets MyProc->waitStatus to STATUS_ERROR, so
|
* RemoveFromWaitQueue sets MyProc->waitStatus to STATUS_ERROR, so
|
||||||
* ProcSleep will report an error after we return from the signal handler.
|
* ProcSleep will report an error after we return from the signal
|
||||||
|
* handler.
|
||||||
*/
|
*/
|
||||||
Assert(MyProc->waitLock != NULL);
|
Assert(MyProc->waitLock != NULL);
|
||||||
RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag)));
|
RemoveFromWaitQueue(MyProc, LockTagHashCode(&(MyProc->waitLock->tag)));
|
||||||
@ -1071,50 +1145,35 @@ CheckDeadLock(void)
|
|||||||
PGSemaphoreUnlock(&MyProc->sem);
|
PGSemaphoreUnlock(&MyProc->sem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We're done here. Transaction abort caused by the error that ProcSleep
|
* We're done here. Transaction abort caused by the error that
|
||||||
* will raise will cause any other locks we hold to be released, thus
|
* ProcSleep will raise will cause any other locks we hold to be
|
||||||
* allowing other processes to wake up; we don't need to do that here.
|
* released, thus allowing other processes to wake up; we don't need
|
||||||
* NOTE: an exception is that releasing locks we hold doesn't consider the
|
* to do that here. NOTE: an exception is that releasing locks we
|
||||||
* possibility of waiters that were blocked behind us on the lock we just
|
* hold doesn't consider the possibility of waiters that were blocked
|
||||||
* failed to get, and might now be wakable because we're not in front of
|
* behind us on the lock we just failed to get, and might now be
|
||||||
* them anymore. However, RemoveFromWaitQueue took care of waking up any
|
* wakable because we're not in front of them anymore. However,
|
||||||
* such processes.
|
* RemoveFromWaitQueue took care of waking up any such processes.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
else if (log_lock_waits)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Unlock my semaphore so that the interrupted ProcSleep() call can
|
||||||
|
* print the log message (we daren't do it here because we are inside
|
||||||
|
* a signal handler). It will then sleep again until someone
|
||||||
|
* releases the lock.
|
||||||
|
*/
|
||||||
|
PGSemaphoreUnlock(&MyProc->sem);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Release locks acquired at head of routine. Order is not critical, so
|
* Release locks acquired at head of routine. Order is not critical, so
|
||||||
* do it back-to-front to avoid waking another CheckDeadLock instance
|
* do it back-to-front to avoid waking another CheckDeadLock instance
|
||||||
* before it can get all the locks.
|
* before it can get all the locks.
|
||||||
*/
|
*/
|
||||||
|
check_done:
|
||||||
for (i = NUM_LOCK_PARTITIONS; --i >= 0;)
|
for (i = NUM_LOCK_PARTITIONS; --i >= 0;)
|
||||||
LWLockRelease(FirstLockMgrLock + i);
|
LWLockRelease(FirstLockMgrLock + i);
|
||||||
|
|
||||||
/*
|
|
||||||
* Issue any log messages requested.
|
|
||||||
*
|
|
||||||
* Deadlock ERROR messages are issued as part of transaction abort, so
|
|
||||||
* these messages should not raise error states intentionally.
|
|
||||||
*/
|
|
||||||
if (log_lock_waits)
|
|
||||||
{
|
|
||||||
switch (deadlock_state)
|
|
||||||
{
|
|
||||||
case DS_SOFT_DEADLOCK:
|
|
||||||
ereport(LOG,
|
|
||||||
(errmsg("deadlock avoided by rearranging lock order")));
|
|
||||||
break;
|
|
||||||
case DS_DEADLOCK_NOT_FOUND:
|
|
||||||
ereport(LOG,
|
|
||||||
(errmsg("statement waiting for lock for at least %d ms",
|
|
||||||
DeadlockTimeout)));
|
|
||||||
break;
|
|
||||||
case DS_HARD_DEADLOCK:
|
|
||||||
break; /* ERROR message handled during abort */
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1211,8 +1270,8 @@ enable_sig_alarm(int delayms, bool is_statement_timeout)
|
|||||||
* to the state variables. The deadlock checker may get run earlier
|
* to the state variables. The deadlock checker may get run earlier
|
||||||
* than normal, but that does no harm.
|
* than normal, but that does no harm.
|
||||||
*/
|
*/
|
||||||
fin_time = GetCurrentTimestamp();
|
timeout_start_time = GetCurrentTimestamp();
|
||||||
fin_time = TimestampTzPlusMilliseconds(fin_time, delayms);
|
fin_time = TimestampTzPlusMilliseconds(timeout_start_time, delayms);
|
||||||
deadlock_timeout_active = true;
|
deadlock_timeout_active = true;
|
||||||
if (fin_time >= statement_fin_time)
|
if (fin_time >= statement_fin_time)
|
||||||
return true;
|
return true;
|
||||||
@ -1221,6 +1280,9 @@ enable_sig_alarm(int delayms, bool is_statement_timeout)
|
|||||||
{
|
{
|
||||||
/* Begin deadlock timeout with no statement-level timeout */
|
/* Begin deadlock timeout with no statement-level timeout */
|
||||||
deadlock_timeout_active = true;
|
deadlock_timeout_active = true;
|
||||||
|
/* GetCurrentTimestamp can be expensive, so only do it if we must */
|
||||||
|
if (log_lock_waits)
|
||||||
|
timeout_start_time = GetCurrentTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we reach here, okay to set the timer interrupt */
|
/* If we reach here, okay to set the timer interrupt */
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.398 2007/06/18 10:02:57 mha Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.399 2007/06/19 20:13:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -825,13 +825,14 @@ static struct config_bool ConfigureNamesBool[] =
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
{"log_lock_waits", PGC_SIGHUP, LOGGING_WHAT,
|
{"log_lock_waits", PGC_SUSET, LOGGING_WHAT,
|
||||||
gettext_noop("Logs long lock wait events."),
|
gettext_noop("Logs long lock waits."),
|
||||||
NULL
|
NULL
|
||||||
},
|
},
|
||||||
&log_lock_waits,
|
&log_lock_waits,
|
||||||
false, NULL, NULL
|
false, NULL, NULL
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
|
{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
|
||||||
gettext_noop("Logs the host name in the connection logs."),
|
gettext_noop("Logs the host name in the connection logs."),
|
||||||
|
@ -7,13 +7,14 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.57 2007/01/05 22:19:58 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.58 2007/06/19 20:13:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef LMGR_H
|
#ifndef LMGR_H
|
||||||
#define LMGR_H
|
#define LMGR_H
|
||||||
|
|
||||||
|
#include "lib/stringinfo.h"
|
||||||
#include "storage/lock.h"
|
#include "storage/lock.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
@ -69,4 +70,7 @@ extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
|
|||||||
/* Knowledge about which locktags describe temp objects */
|
/* Knowledge about which locktags describe temp objects */
|
||||||
extern bool LockTagIsTemp(const LOCKTAG *tag);
|
extern bool LockTagIsTemp(const LOCKTAG *tag);
|
||||||
|
|
||||||
|
/* Describe a locktag for error messages */
|
||||||
|
extern void DescribeLockTag(StringInfo buf, const LOCKTAG *tag);
|
||||||
|
|
||||||
#endif /* LMGR_H */
|
#endif /* LMGR_H */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.105 2007/05/30 16:16:32 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.106 2007/06/19 20:13:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -397,10 +397,12 @@ typedef enum
|
|||||||
/* Deadlock states identified by DeadLockCheck() */
|
/* Deadlock states identified by DeadLockCheck() */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
DS_DEADLOCK_NOT_FOUND, /* no deadlock found within database server */
|
DS_NOT_YET_CHECKED, /* no deadlock check has run yet */
|
||||||
DS_SOFT_DEADLOCK, /* deadlock, but lock queues rearrangeable */
|
DS_NO_DEADLOCK, /* no deadlock detected */
|
||||||
|
DS_SOFT_DEADLOCK, /* deadlock avoided by queue rearrangement */
|
||||||
DS_HARD_DEADLOCK /* deadlock, no way out but ERROR */
|
DS_HARD_DEADLOCK /* deadlock, no way out but ERROR */
|
||||||
} DeadlockState;
|
} DeadLockState;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The lockmgr's shared hash tables are partitioned to reduce contention.
|
* The lockmgr's shared hash tables are partitioned to reduce contention.
|
||||||
@ -449,7 +451,7 @@ extern void lock_twophase_postcommit(TransactionId xid, uint16 info,
|
|||||||
extern void lock_twophase_postabort(TransactionId xid, uint16 info,
|
extern void lock_twophase_postabort(TransactionId xid, uint16 info,
|
||||||
void *recdata, uint32 len);
|
void *recdata, uint32 len);
|
||||||
|
|
||||||
extern DeadlockState DeadLockCheck(PGPROC *proc);
|
extern DeadLockState DeadLockCheck(PGPROC *proc);
|
||||||
extern void DeadLockReport(void);
|
extern void DeadLockReport(void);
|
||||||
extern void RememberSimpleDeadLock(PGPROC *proc1,
|
extern void RememberSimpleDeadLock(PGPROC *proc1,
|
||||||
LOCKMODE lockmode,
|
LOCKMODE lockmode,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user