From 5b4994cc6c244ecc6eb6826f91a35cbd07a9f00e Mon Sep 17 00:00:00 2001 From: Anthony Hsu Date: Sun, 6 Jul 2025 11:20:50 -0700 Subject: [PATCH v1] Set 1s WaitLatch timeout if standby limit has expired in ResolveRecoveryConflictWithBufferPin --- src/backend/storage/ipc/standby.c | 16 ++++++++++------ src/backend/storage/lmgr/proc.c | 19 ++++++++++++++++--- src/include/storage/proc.h | 2 ++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 7fa8d9247e0..ff9070aba35 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -793,12 +793,14 @@ void ResolveRecoveryConflictWithBufferPin(void) { TimestampTz ltime; + bool standby_limit_expired; Assert(InHotStandby); ltime = GetStandbyLimitTime(); + standby_limit_expired = GetCurrentTimestamp() >= ltime && ltime != 0; - if (GetCurrentTimestamp() >= ltime && ltime != 0) + if (standby_limit_expired) { /* * We're already behind, so clear a path as quickly as possible. @@ -835,12 +837,14 @@ ResolveRecoveryConflictWithBufferPin(void) * Wait to be signaled by UnpinBuffer() or for the wait to be interrupted * by one of the timeouts established above. * - * We assume that only UnpinBuffer() and the timeout requests established - * above can wake us up here. WakeupRecovery() called by walreceiver or - * SIGHUP signal handler, etc cannot do that because it uses the different - * latch from that ProcWaitForSignal() waits on. + * If the standby limit has already expired, we also set a 1s timeout. This + * is to ensure the startup process is still woken up in a reasonable time + * in case a new backend sneaks in and acquires a conflicting pin before the + * original conflicting backends are able to cancel themselves and reduce + * the pin count to 1 (which wakes up the startup process). */ - ProcWaitForSignal(WAIT_EVENT_BUFFER_PIN); + ProcWaitForSignalWithTimeout(WAIT_EVENT_BUFFER_PIN, + standby_limit_expired ? 1000 : 0); if (got_standby_delay_timeout) SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index e9ef0fbfe32..7114256e9ed 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -1965,16 +1965,29 @@ GetLockHoldersAndWaiters(LOCALLOCK *locallock, StringInfo lock_holders_sbuf, /* * ProcWaitForSignal - wait for a signal from another backend. + */ +void +ProcWaitForSignal(uint32 wait_event_info) +{ + ProcWaitForSignalWithTimeout(wait_event_info, 0); +} + +/* + * ProcWaitForSignalWithTimeout - wait for a signal from another backend with a + * timeout. * * As this uses the generic process latch the caller has to be robust against * unrelated wakeups: Always check that the desired state has occurred, and * wait again if not. */ void -ProcWaitForSignal(uint32 wait_event_info) +ProcWaitForSignalWithTimeout(uint32 wait_event_info, long timeout_ms) { - (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0, - wait_event_info); + int wake_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH; + if (timeout_ms > 0) + wake_events |= WL_TIMEOUT; + + (void) WaitLatch(MyLatch, wake_events, timeout_ms, wait_event_info); ResetLatch(MyLatch); CHECK_FOR_INTERRUPTS(); } diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 9f9b3fcfbf1..930510ca8e5 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -505,6 +505,8 @@ extern void GetLockHoldersAndWaiters(LOCALLOCK *locallock, int *lockHoldersNum); extern void ProcWaitForSignal(uint32 wait_event_info); +extern void ProcWaitForSignalWithTimeout(uint32 wait_event_info, + long timeout_ms); extern void ProcSendSignal(ProcNumber procNumber); extern PGPROC *AuxiliaryPidGetProc(int pid); -- 2.50.0.727.gbf7dc18ff4-goog