From 7f07c21f247fa6d900c82ececb2a48f879ce5814 Mon Sep 17 00:00:00 2001 From: Andrey Borodin Date: Tue, 1 Sep 2026 23:58:07 +0500 Subject: [PATCH v1 3/3] Fix postmaster hang on shutdown requested during crash restart If a smart or fast shutdown request arrives while the postmaster is still reinitializing after a crash -- that is, while FatalError is set and before the startup process has signalled PMSIGNAL_RECOVERY_STARTED -- then process_pm_shutdown_request() moved the state machine to PM_STOP_BACKENDS and signalled the children with SIGTERM. The checkpointer relaunched for crash recovery ignores SIGTERM, so the postmaster then waited for it in PM_WAIT_BACKENDS forever. There is no consistent state to preserve in the middle of crash recovery, so in that situation terminate the children the same crash-style way the postmaster already uses elsewhere, via HandleFatalError(), which sends SIGQUIT and heads for PM_WAIT_BACKENDS / PM_NO_CHILDREN. Immediate shutdown was already handled this way; this brings the smart and fast paths in line for the crash-restart window. This is distinct from commit ead8f696b7cd, which handles the startup process *crashing* during crash restart; here the startup process is healthy and the trigger is an incoming shutdown request. Reported-by: Sergey Shinderuk Discussion: https://postgr.es/m/63dcad16-22de-4326-a395-5310bc7e05ff@postgrespro.ru --- src/backend/postmaster/postmaster.c | 35 +++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 5f7a7b368e1..aaf8873c0e7 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -2156,8 +2156,16 @@ process_pm_shutdown_request(void) connsAllowed = false; else if (pmState == PM_STARTUP || pmState == PM_RECOVERY) { - /* There should be no clients, so proceed to stop children */ - UpdatePMState(PM_STOP_BACKENDS); + if (FatalError) + { + /* Crash recovery in progress; see the FastShutdown case */ + HandleFatalError(PMQUIT_FOR_STOP, false); + } + else + { + /* No clients, so proceed to stop children */ + UpdatePMState(PM_STOP_BACKENDS); + } } /* @@ -2190,8 +2198,27 @@ process_pm_shutdown_request(void) if (pmState == PM_STARTUP || pmState == PM_RECOVERY) { - /* Just shut down background processes silently */ - UpdatePMState(PM_STOP_BACKENDS); + if (FatalError) + { + /* + * We are reinitializing after a crash, and the shutdown + * request arrived before the startup process signalled + * PMSIGNAL_RECOVERY_STARTED, which is when FatalError + * would have been cleared. The auxiliary processes + * relaunched for crash recovery expect crash-style + * signalling: in particular the checkpointer ignores + * SIGTERM, so the regular PM_STOP_BACKENDS path would + * wait for it forever. There is no consistent state to + * save mid-recovery anyway, so terminate the children the + * same way the crash path does and head for exit. + */ + HandleFatalError(PMQUIT_FOR_STOP, false); + } + else + { + /* Just shut down background processes silently */ + UpdatePMState(PM_STOP_BACKENDS); + } } else if (pmState == PM_RUN || pmState == PM_HOT_STANDBY) -- That's all, folks. May the source be with you.