Report the current queries of all backends involved in a deadlock
(if they'd be visible to the current user in pg_stat_activity). This might look like it's subject to race conditions, but it's actually pretty safe because at the time DeadLockReport() is constructing the report, we haven't yet aborted our transaction and so we can expect that everyone else involved in the deadlock is still blocked on some lock. (There are corner cases where that might not be true, such as a statement timeout triggering in another backend before we finish reporting; but at worst we'd report a misleading activity string, so it seems acceptable considering the usefulness of reporting the queries.) Original patch by Itagaki Takahiro, heavily modified by me.
This commit is contained in:
parent
df812e91ad
commit
4b7ae4afae
@ -13,7 +13,7 @@
|
||||
*
|
||||
* Copyright (c) 2001-2008, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.169 2008/01/01 19:45:51 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.170 2008/03/21 21:08:31 tgl Exp $
|
||||
* ----------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
@ -2036,6 +2036,80 @@ pgstat_read_current_status(void)
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* pgstat_get_backend_current_activity() -
|
||||
*
|
||||
* Return a string representing the current activity of the backend with
|
||||
* the specified PID. This looks directly at the BackendStatusArray,
|
||||
* and so will provide current information regardless of the age of our
|
||||
* transaction's snapshot of the status array.
|
||||
*
|
||||
* It is the caller's responsibility to invoke this only for backends whose
|
||||
* state is expected to remain stable while the result is in use. The
|
||||
* only current use is in deadlock reporting, where we can expect that
|
||||
* the target backend is blocked on a lock. (There are corner cases
|
||||
* where the target's wait could get aborted while we are looking at it,
|
||||
* but the very worst consequence is to return a pointer to a string
|
||||
* that's been changed, so we won't worry too much.)
|
||||
*
|
||||
* Note: return strings for special cases match pg_stat_get_backend_activity.
|
||||
* ----------
|
||||
*/
|
||||
const char *
|
||||
pgstat_get_backend_current_activity(int pid)
|
||||
{
|
||||
PgBackendStatus *beentry;
|
||||
int i;
|
||||
|
||||
beentry = BackendStatusArray;
|
||||
for (i = 1; i <= MaxBackends; i++)
|
||||
{
|
||||
/*
|
||||
* Although we expect the target backend's entry to be stable, that
|
||||
* doesn't imply that anyone else's is. To avoid identifying the
|
||||
* wrong backend, while we check for a match to the desired PID we
|
||||
* must follow the protocol of retrying if st_changecount changes
|
||||
* while we examine the entry, or if it's odd. (This might be
|
||||
* unnecessary, since fetching or storing an int is almost certainly
|
||||
* atomic, but let's play it safe.) We use a volatile pointer here
|
||||
* to ensure the compiler doesn't try to get cute.
|
||||
*/
|
||||
volatile PgBackendStatus *vbeentry = beentry;
|
||||
bool found;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int save_changecount = vbeentry->st_changecount;
|
||||
|
||||
found = (vbeentry->st_procpid == pid);
|
||||
|
||||
if (save_changecount == vbeentry->st_changecount &&
|
||||
(save_changecount & 1) == 0)
|
||||
break;
|
||||
|
||||
/* Make sure we can break out of loop if stuck... */
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
/* Now it is safe to use the non-volatile pointer */
|
||||
if (!superuser() && beentry->st_userid != GetUserId())
|
||||
return "<insufficient privilege>";
|
||||
else if (*(beentry->st_activity) == '\0')
|
||||
return "<command string not enabled>";
|
||||
else
|
||||
return beentry->st_activity;
|
||||
}
|
||||
|
||||
beentry++;
|
||||
}
|
||||
|
||||
/* If we get here, caller is in error ... */
|
||||
return "<backend information not available>";
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Local support functions follow
|
||||
* ------------------------------------------------------------
|
||||
|
@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.51 2008/01/01 19:45:52 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.52 2008/03/21 21:08:31 tgl Exp $
|
||||
*
|
||||
* Interface:
|
||||
*
|
||||
@ -26,6 +26,7 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "storage/lmgr.h"
|
||||
#include "storage/proc.h"
|
||||
#include "utils/memutils.h"
|
||||
@ -878,12 +879,14 @@ PrintLockQueue(LOCK *lock, const char *info)
|
||||
void
|
||||
DeadLockReport(void)
|
||||
{
|
||||
StringInfoData buf;
|
||||
StringInfoData buf2;
|
||||
StringInfoData detailbuf;
|
||||
StringInfoData contextbuf;
|
||||
StringInfoData locktagbuf;
|
||||
int i;
|
||||
|
||||
initStringInfo(&buf);
|
||||
initStringInfo(&buf2);
|
||||
initStringInfo(&detailbuf);
|
||||
initStringInfo(&contextbuf);
|
||||
initStringInfo(&locktagbuf);
|
||||
|
||||
for (i = 0; i < nDeadlockDetails; i++)
|
||||
{
|
||||
@ -896,26 +899,36 @@ DeadLockReport(void)
|
||||
else
|
||||
nextpid = deadlockDetails[0].pid;
|
||||
|
||||
/* reset locktagbuf to hold next object description */
|
||||
resetStringInfo(&locktagbuf);
|
||||
|
||||
DescribeLockTag(&locktagbuf, &info->locktag);
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfoChar(&buf, '\n');
|
||||
appendStringInfoChar(&detailbuf, '\n');
|
||||
|
||||
/* reset buf2 to hold next object description */
|
||||
resetStringInfo(&buf2);
|
||||
|
||||
DescribeLockTag(&buf2, &info->locktag);
|
||||
|
||||
appendStringInfo(&buf,
|
||||
appendStringInfo(&detailbuf,
|
||||
_("Process %d waits for %s on %s; blocked by process %d."),
|
||||
info->pid,
|
||||
GetLockmodeName(info->locktag.locktag_lockmethodid,
|
||||
info->lockmode),
|
||||
buf2.data,
|
||||
locktagbuf.data,
|
||||
nextpid);
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfoChar(&contextbuf, '\n');
|
||||
|
||||
appendStringInfo(&contextbuf,
|
||||
_("Process %d: %s"),
|
||||
info->pid,
|
||||
pgstat_get_backend_current_activity(info->pid));
|
||||
}
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_T_R_DEADLOCK_DETECTED),
|
||||
errmsg("deadlock detected"),
|
||||
errdetail("%s", buf.data)));
|
||||
errdetail("%s", detailbuf.data),
|
||||
errcontext("%s", contextbuf.data)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* Copyright (c) 2001-2008, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/pgstat.h,v 1.71 2008/01/01 19:45:56 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/pgstat.h,v 1.72 2008/03/21 21:08:31 tgl Exp $
|
||||
* ----------
|
||||
*/
|
||||
#ifndef PGSTAT_H
|
||||
@ -507,6 +507,7 @@ extern void pgstat_bestart(void);
|
||||
extern void pgstat_report_activity(const char *what);
|
||||
extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
|
||||
extern void pgstat_report_waiting(bool waiting);
|
||||
extern const char *pgstat_get_backend_current_activity(int pid);
|
||||
|
||||
extern void pgstat_initstats(Relation rel);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user