summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2025-08-29 15:43:34 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2025-08-29 15:43:34 -0400
commitf727b63e810724c7187f38b2580b2915bdbc3c9c (patch)
tree69c292fc1218f0cee68fba0baa3547e88f354dfd /src
parente686010c5b47f2e7f7e4e8d31ef69efadbbc4d72 (diff)
Provide error context when an error is thrown within WaitOnLock().
Show the requested lock level and the object being waited on, in the same format we use for deadlock reports and similar errors. This is particularly helpful for debugging lock-timeout errors, since otherwise the user has very little to go on about which lock timed out. The performance cost of setting up the callback should be negligible compared to the other tracing support already present in WaitOnLock. As in the deadlock-report case, we just show numeric object OIDs, because it seems too scary to try to perform catalog lookups in this context. Reported-by: Steve Baldwin <steve.baldwin@gmail.com> Author: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/1602369.1752167154@sss.pgh.pa.us
Diffstat (limited to 'src')
-rw-r--r--src/backend/storage/lmgr/lock.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 233b85b623d..4cc7f645c31 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -415,6 +415,7 @@ static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
static void BeginStrongLockAcquire(LOCALLOCK *locallock, uint32 fasthashcode);
static void FinishStrongLockAcquire(void);
static ProcWaitStatus WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
+static void waitonlock_error_callback(void *arg);
static void ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock);
static void LockReassignOwner(LOCALLOCK *locallock, ResourceOwner parent);
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
@@ -1931,6 +1932,7 @@ static ProcWaitStatus
WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
{
ProcWaitStatus result;
+ ErrorContextCallback waiterrcontext;
TRACE_POSTGRESQL_LOCK_WAIT_START(locallock->tag.lock.locktag_field1,
locallock->tag.lock.locktag_field2,
@@ -1939,6 +1941,12 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
locallock->tag.lock.locktag_type,
locallock->tag.mode);
+ /* Setup error traceback support for ereport() */
+ waiterrcontext.callback = waitonlock_error_callback;
+ waiterrcontext.arg = (void *) locallock;
+ waiterrcontext.previous = error_context_stack;
+ error_context_stack = &waiterrcontext;
+
/* adjust the process title to indicate that it's waiting */
set_ps_display_suffix("waiting");
@@ -1990,6 +1998,8 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
/* reset ps display to remove the suffix */
set_ps_display_remove_suffix();
+ error_context_stack = waiterrcontext.previous;
+
TRACE_POSTGRESQL_LOCK_WAIT_DONE(locallock->tag.lock.locktag_field1,
locallock->tag.lock.locktag_field2,
locallock->tag.lock.locktag_field3,
@@ -2001,6 +2011,28 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
}
/*
+ * error context callback for failures in WaitOnLock
+ *
+ * We report which lock was being waited on, in the same style used in
+ * deadlock reports. This helps with lock timeout errors in particular.
+ */
+static void
+waitonlock_error_callback(void *arg)
+{
+ LOCALLOCK *locallock = (LOCALLOCK *) arg;
+ const LOCKTAG *tag = &locallock->tag.lock;
+ LOCKMODE mode = locallock->tag.mode;
+ StringInfoData locktagbuf;
+
+ initStringInfo(&locktagbuf);
+ DescribeLockTag(&locktagbuf, tag);
+
+ errcontext("waiting for %s on %s",
+ GetLockmodeName(tag->locktag_lockmethodid, mode),
+ locktagbuf.data);
+}
+
+/*
* Remove a proc from the wait-queue it is on (caller must know it is on one).
* This is only used when the proc has failed to get the lock, so we set its
* waitStatus to PROC_WAIT_STATUS_ERROR.