From 0165f38d11ec00a0efacccb746c2d1d6309b0f0b Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Mon, 28 Sep 2026 14:22:04 +0900 Subject: [PATCH] Prevent stale pg_locks.waitstart values Previously, pg_locks.waitstart could report the start time of a previous lock wait for a new wait. For a regular backend, this could happen briefly before the new start time was stored. On a standby, the startup process could show the old time throughout the next wait, making it appear to have started earlier than it did. This happened because PGPROC->waitStart could remain set after a previous wait in two cases. First, RemoveFromWaitQueue() did not clear it when a wait was canceled due to a lock timeout, cancellation, or deadlock. Second, even after a successful lock grant, ProcWakeup() could clear it before the waiter stored its new start time. Fix this by clearing waitStart in RemoveFromWaitQueue() when a failed wait is removed, at the end of ProcSleep() to handle writes made after ProcWakeup() cleared it, and in LockErrorCleanup() when an error bypasses the ProcSleep() reset. This prevents pg_locks.waitstart from showing the previous wait's time for a new wait. Backpatch to all supported versions. Author: Shihao Zhong Reported-by: Alex Shapalov Reviewed-by: Chao Li Reviewed-by: Michael Paquier Reviewed-by: Fujii Masao Reviewed-by: Andrew Krylosov Discussion: https://postgr.es/m/CAPrb+Q+XN=sNusXiUeWmMo2H7Qgq3Y4uPekSSLkHcnCyf7GhXg@mail.gmail.com Discussion: https://postgr.es/m/CAGRkXqQLxZBr-ouVrtaX2utMggi4+TiVMbgH04b_0JLgKryxbA@mail.gmail.com Backpatch-through: 14 --- src/backend/storage/lmgr/lock.c | 1 + src/backend/storage/lmgr/proc.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index e5e7ab55716..f29a9675352 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -1936,6 +1936,7 @@ RemoveFromWaitQueue(PGPROC *proc, uint32 hashcode) proc->waitLock = NULL; proc->waitProcLock = NULL; proc->waitStatus = PROC_WAIT_STATUS_ERROR; + pg_atomic_write_u64(&proc->waitStart, 0); /* * Delete the proclock immediately if it represents no already-held locks. diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index d204fa1dd22..01300c5d9f9 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -783,6 +783,9 @@ LockErrorCleanup(void) GrantAwaitedLock(); } + /* An error can bypass ProcSleep()'s reset, so clear waitStart here */ + pg_atomic_write_u64(&MyProc->waitStart, 0); + lockAwaited = NULL; LWLockRelease(partitionLock); @@ -1643,6 +1646,12 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, bool dontWait) } } while (myWaitStatus == PROC_WAIT_STATUS_WAITING); + /* + * Clear waitStart after the wait, as we may have set it after + * ProcWakeup() cleared it. + */ + pg_atomic_write_u64(&MyProc->waitStart, 0); + /* * Disable the timers, if they are still running. As in LockErrorCleanup, * we must preserve the LOCK_TIMEOUT indicator flag: if a lock timeout has