From 8b7468611abcd6c858e55146784c8fdc1cd5cc9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Herrera?= Date: Tue, 15 Jul 2025 18:19:27 +0200 Subject: [PATCH 1/2] Create a separate file listing backend types Use our established coding pattern to reduce maintenance pain when adding other per-process-type characteristics. Like PG_KEYWORD, PG_CMDTAG, PG_RMGR. --- src/backend/postmaster/launch_backend.c | 32 ++------------ src/backend/utils/init/miscinit.c | 59 ++----------------------- src/include/postmaster/proctypelist.h | 50 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 83 deletions(-) create mode 100644 src/include/postmaster/proctypelist.h diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index bf6b55ee8304..8b2f1a0cf41f 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -177,34 +177,10 @@ typedef struct } child_process_kind; static child_process_kind child_process_kinds[] = { - [B_INVALID] = {"invalid", NULL, false}, - - [B_BACKEND] = {"backend", BackendMain, true}, - [B_DEAD_END_BACKEND] = {"dead-end backend", BackendMain, true}, - [B_AUTOVAC_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true}, - [B_AUTOVAC_WORKER] = {"autovacuum worker", AutoVacWorkerMain, true}, - [B_BG_WORKER] = {"bgworker", BackgroundWorkerMain, true}, - - /* - * WAL senders start their life as regular backend processes, and change - * their type after authenticating the client for replication. We list it - * here for PostmasterChildName() but cannot launch them directly. - */ - [B_WAL_SENDER] = {"wal sender", NULL, true}, - [B_SLOTSYNC_WORKER] = {"slot sync worker", ReplSlotSyncWorkerMain, true}, - - [B_STANDALONE_BACKEND] = {"standalone backend", NULL, false}, - - [B_ARCHIVER] = {"archiver", PgArchiverMain, true}, - [B_BG_WRITER] = {"bgwriter", BackgroundWriterMain, true}, - [B_CHECKPOINTER] = {"checkpointer", CheckpointerMain, true}, - [B_IO_WORKER] = {"io_worker", IoWorkerMain, true}, - [B_STARTUP] = {"startup", StartupProcessMain, true}, - [B_WAL_RECEIVER] = {"wal_receiver", WalReceiverMain, true}, - [B_WAL_SUMMARIZER] = {"wal_summarizer", WalSummarizerMain, true}, - [B_WAL_WRITER] = {"wal_writer", WalWriterMain, true}, - - [B_LOGGER] = {"syslogger", SysLoggerMain, false}, +#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \ + [bktype] = {description, main_func, shmem_attach}, +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE }; const char * diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 545d1e90fbd4..749fb502e95e 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -266,62 +266,11 @@ GetBackendTypeDesc(BackendType backendType) switch (backendType) { - case B_INVALID: - backendDesc = gettext_noop("not initialized"); - break; - case B_ARCHIVER: - backendDesc = gettext_noop("archiver"); - break; - case B_AUTOVAC_LAUNCHER: - backendDesc = gettext_noop("autovacuum launcher"); - break; - case B_AUTOVAC_WORKER: - backendDesc = gettext_noop("autovacuum worker"); - break; - case B_BACKEND: - backendDesc = gettext_noop("client backend"); - break; - case B_DEAD_END_BACKEND: - backendDesc = gettext_noop("dead-end client backend"); - break; - case B_BG_WORKER: - backendDesc = gettext_noop("background worker"); - break; - case B_BG_WRITER: - backendDesc = gettext_noop("background writer"); - break; - case B_CHECKPOINTER: - backendDesc = gettext_noop("checkpointer"); - break; - case B_IO_WORKER: - backendDesc = gettext_noop("io worker"); - break; - case B_LOGGER: - backendDesc = gettext_noop("logger"); - break; - case B_SLOTSYNC_WORKER: - backendDesc = gettext_noop("slotsync worker"); - break; - case B_STANDALONE_BACKEND: - backendDesc = gettext_noop("standalone backend"); - break; - case B_STARTUP: - backendDesc = gettext_noop("startup"); - break; - case B_WAL_RECEIVER: - backendDesc = gettext_noop("walreceiver"); - break; - case B_WAL_SENDER: - backendDesc = gettext_noop("walsender"); - break; - case B_WAL_SUMMARIZER: - backendDesc = gettext_noop("walsummarizer"); - break; - case B_WAL_WRITER: - backendDesc = gettext_noop("walwriter"); - break; +#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \ + case bktype: backendDesc = gettext_noop(description); break; +#include "postmaster/proctypelist.h" +#undef PG_PROCTYPE } - return backendDesc; } diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h new file mode 100644 index 000000000000..919f00bb3a78 --- /dev/null +++ b/src/include/postmaster/proctypelist.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + * + * proctypelist.h + * + * The list of process types is kept on its own source file for use by + * automatic tools. The exact representation of a process type is + * determined by the PG_PROCTYPE macro, which is not defined in this + * file; it can be defined by the caller for special purposes. + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/include/postmaster/proctypelist.h + * + *------------------------------------------------------------------------- + */ + +/* there is deliberately not an #ifndef PROCTYPELIST_H here */ + +/* + * WAL senders start their life as regular backend processes, and change their + * type after authenticating the client for replication. We list it here for + * PostmasterChildName() but cannot launch them directly. + */ + +/* + * List of process types (symbol, description, Main function, shmem_attach) + * entries. + */ + +/* bktype, description, main_func, shmem_attach */ +PG_PROCTYPE(B_ARCHIVER, "archiver", PgArchiverMain, true) +PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum launcher", AutoVacLauncherMain, true) +PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum worker", AutoVacWorkerMain, true) +PG_PROCTYPE(B_BACKEND, "client backend", BackendMain, true) +PG_PROCTYPE(B_BG_WORKER, "background worker", BackgroundWorkerMain, true) +PG_PROCTYPE(B_BG_WRITER, "background writer", BackgroundWriterMain, true) +PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", CheckpointerMain, true) +PG_PROCTYPE(B_DEAD_END_BACKEND, "dead-end client backend", BackendMain, true) +PG_PROCTYPE(B_INVALID, "unrecognized", NULL, false) +PG_PROCTYPE(B_IO_WORKER, "io worker", IoWorkerMain, true) +PG_PROCTYPE(B_LOGGER, "syslogger", SysLoggerMain, false) +PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsync worker", ReplSlotSyncWorkerMain, true) +PG_PROCTYPE(B_STANDALONE_BACKEND, "standalone backend", NULL, false) +PG_PROCTYPE(B_STARTUP, "startup", StartupProcessMain, true) +PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", WalReceiverMain, true) +PG_PROCTYPE(B_WAL_SENDER, "walsender", NULL, true) +PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", WalSummarizerMain, true) +PG_PROCTYPE(B_WAL_WRITER, "walwriter", WalWriterMain, true) From 2bc0aff255575ac0af5832970b4150a9f771d169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Herrera?= Date: Mon, 28 Jul 2025 20:56:20 +0200 Subject: [PATCH 2/2] LogChildExit / HandleChildCrash support (Didn't actually test that bgworkers are doing the expected thing!) --- src/backend/postmaster/postmaster.c | 121 +++++++++--------- src/test/perl/PostgreSQL/Test/Cluster.pm | 2 +- .../postmaster/t/002_connection_limits.pl | 2 +- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index e01d9f0cfe81..935129515095 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -428,8 +428,8 @@ static void process_pm_reload_request(void); static void process_pm_shutdown_request(void); static void dummy_handler(SIGNAL_ARGS); static void CleanupBackend(PMChild *bp, int exitstatus); -static void HandleChildCrash(int pid, int exitstatus, const char *procname); -static void LogChildExit(int lev, const char *procname, +static void HandleChildCrash(int pid, int exitstatus, BackendType proctype, const char *addtype); +static void LogChildExit(int lev, BackendType proctype, const char *addtype, int pid, int exitstatus); static void PostmasterStateMachine(void); static void UpdatePMState(PMState newState); @@ -2285,8 +2285,7 @@ process_pm_child_exit(void) StartupStatus != STARTUP_SIGNALED && !EXIT_STATUS_0(exitstatus)) { - LogChildExit(LOG, _("startup process"), - pid, exitstatus); + LogChildExit(LOG, B_STARTUP, NULL, pid, exitstatus); ereport(LOG, (errmsg("aborting startup due to startup process failure"))); ExitPostmaster(1); @@ -2320,8 +2319,7 @@ process_pm_child_exit(void) } else StartupStatus = STARTUP_CRASHED; - HandleChildCrash(pid, exitstatus, - _("startup process")); + HandleChildCrash(pid, exitstatus, B_STARTUP, NULL); continue; } @@ -2365,8 +2363,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(BgWriterPMChild); BgWriterPMChild = NULL; if (!EXIT_STATUS_0(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("background writer process")); + HandleChildCrash(pid, exitstatus, B_BG_WRITER, NULL); continue; } @@ -2398,8 +2395,7 @@ process_pm_child_exit(void) * Any unexpected exit of the checkpointer (including FATAL * exit) is treated as a crash. */ - HandleChildCrash(pid, exitstatus, - _("checkpointer process")); + HandleChildCrash(pid, exitstatus, B_CHECKPOINTER, NULL); } continue; @@ -2415,8 +2411,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(WalWriterPMChild); WalWriterPMChild = NULL; if (!EXIT_STATUS_0(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("WAL writer process")); + HandleChildCrash(pid, exitstatus, B_WAL_WRITER, NULL); continue; } @@ -2431,8 +2426,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(WalReceiverPMChild); WalReceiverPMChild = NULL; if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("WAL receiver process")); + HandleChildCrash(pid, exitstatus, B_WAL_RECEIVER, NULL); continue; } @@ -2446,8 +2440,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(WalSummarizerPMChild); WalSummarizerPMChild = NULL; if (!EXIT_STATUS_0(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("WAL summarizer process")); + HandleChildCrash(pid, exitstatus, B_WAL_SUMMARIZER, NULL); continue; } @@ -2462,8 +2455,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(AutoVacLauncherPMChild); AutoVacLauncherPMChild = NULL; if (!EXIT_STATUS_0(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("autovacuum launcher process")); + HandleChildCrash(pid, exitstatus, B_AUTOVAC_LAUNCHER, NULL); continue; } @@ -2478,8 +2470,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(PgArchPMChild); PgArchPMChild = NULL; if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("archiver process")); + HandleChildCrash(pid, exitstatus, B_ARCHIVER, NULL); continue; } @@ -2494,8 +2485,7 @@ process_pm_child_exit(void) StartSysLogger(); if (!EXIT_STATUS_0(exitstatus)) - LogChildExit(LOG, _("system logger process"), - pid, exitstatus); + LogChildExit(LOG, B_LOGGER, NULL, pid, exitstatus); continue; } @@ -2511,8 +2501,7 @@ process_pm_child_exit(void) ReleasePostmasterChildSlot(SlotSyncWorkerPMChild); SlotSyncWorkerPMChild = NULL; if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) - HandleChildCrash(pid, exitstatus, - _("slot sync worker process")); + HandleChildCrash(pid, exitstatus, B_SLOTSYNC_WORKER, NULL); continue; } @@ -2520,7 +2509,7 @@ process_pm_child_exit(void) if (maybe_reap_io_worker(pid)) { if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) - HandleChildCrash(pid, exitstatus, _("io worker")); + HandleChildCrash(pid, exitstatus, B_IO_WORKER, NULL); maybe_adjust_io_workers(); continue; @@ -2542,9 +2531,9 @@ process_pm_child_exit(void) else { if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) - HandleChildCrash(pid, exitstatus, _("untracked child process")); + HandleChildCrash(pid, exitstatus, B_INVALID, NULL); else - LogChildExit(LOG, _("untracked child process"), pid, exitstatus); + LogChildExit(LOG, B_INVALID, NULL, pid, exitstatus); } } /* loop over pending child-death reports */ @@ -2565,8 +2554,8 @@ static void CleanupBackend(PMChild *bp, int exitstatus) /* child's exit status. */ { - char namebuf[MAXPGPATH]; - const char *procname; + char namebuf[MAXPGPATH]; + char *procname; bool crashed = false; bool logged = false; pid_t bp_pid; @@ -2575,14 +2564,13 @@ CleanupBackend(PMChild *bp, RegisteredBgWorker *rw; /* Construct a process name for the log message */ - if (bp->bkend_type == B_BG_WORKER) + if (bp && bp->bkend_type == B_BG_WORKER && bp->rw) { - snprintf(namebuf, MAXPGPATH, _("background worker \"%s\""), - bp->rw->rw_worker.bgw_type); + strlcpy(namebuf, bp->rw->rw_worker.bgw_type, MAXPGPATH); procname = namebuf; } else - procname = _(GetBackendTypeDesc(bp->bkend_type)); + procname = NULL; /* * If a backend dies in an ugly way then we must signal all other backends @@ -2604,7 +2592,7 @@ CleanupBackend(PMChild *bp, */ if (exitstatus == ERROR_WAIT_NO_CHILDREN) { - LogChildExit(LOG, procname, bp->pid, exitstatus); + LogChildExit(LOG, bp->bkend_type, procname, bp->pid, exitstatus); logged = true; crashed = false; } @@ -2639,7 +2627,7 @@ CleanupBackend(PMChild *bp, */ if (crashed) { - HandleChildCrash(bp_pid, exitstatus, procname); + HandleChildCrash(bp_pid, exitstatus, bp_bkend_type, procname); return; } @@ -2677,7 +2665,7 @@ CleanupBackend(PMChild *bp, if (!logged) { LogChildExit(EXIT_STATUS_0(exitstatus) ? DEBUG1 : LOG, - procname, bp_pid, exitstatus); + bp_bkend_type, procname, bp_pid, exitstatus); logged = true; } @@ -2686,7 +2674,7 @@ CleanupBackend(PMChild *bp, } if (!logged) - LogChildExit(DEBUG2, procname, bp_pid, exitstatus); + LogChildExit(DEBUG2, bp_bkend_type, procname, bp_pid, exitstatus); } /* @@ -2778,8 +2766,8 @@ HandleFatalError(QuitSignalReason reason, bool consider_sigabrt) } /* - * HandleChildCrash -- cleanup after failed backend, bgwriter, checkpointer, - * walwriter, autovacuum, archiver, slot sync worker, or background worker. + * HandleChildCrash -- cleanup after failed backend or certain auxiliary + * processes. * * The objectives here are to clean up our local state about the child * process, and to signal all other remaining children to quickdie. @@ -2787,7 +2775,7 @@ HandleFatalError(QuitSignalReason reason, bool consider_sigabrt) * The caller has already released its PMChild slot. */ static void -HandleChildCrash(int pid, int exitstatus, const char *procname) +HandleChildCrash(int pid, int exitstatus, BackendType proctype, const char *addtype) { /* * We only log messages and send signals if this is the first process @@ -2799,7 +2787,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) if (FatalError || Shutdown == ImmediateShutdown) return; - LogChildExit(LOG, procname, pid, exitstatus); + LogChildExit(LOG, proctype, addtype, pid, exitstatus); ereport(LOG, (errmsg("terminating any other active server processes"))); @@ -2812,9 +2800,13 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) /* * Log the death of a child process. + * + * 'addtype' is an additional word or short phrase that describes the process, + * such as a background worker 'type'. */ static void -LogChildExit(int lev, const char *procname, int pid, int exitstatus) +LogChildExit(int lev, BackendType proctype, const char *addtype, int pid, + int exitstatus) { /* * size of activity_buffer is arbitrary, but set equal to default @@ -2829,14 +2821,13 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus) sizeof(activity_buffer)); if (WIFEXITED(exitstatus)) - ereport(lev, + ereport(lev, addtype ? + errmsg("\"%s\" process of type \"%s\" (PID %d) exited with exit code %d", + GetBackendTypeDesc(proctype), addtype, pid, WEXITSTATUS(exitstatus)) : + errmsg("process of type \"%s\" (PID %d) exited with exit code %d", + GetBackendTypeDesc(proctype), pid, WEXITSTATUS(exitstatus)), - /*------ - translator: %s is a noun phrase describing a child process, such as - "server process" */ - (errmsg("%s (PID %d) exited with exit code %d", - procname, pid, WEXITSTATUS(exitstatus)), - activity ? errdetail("Failed process was running: %s", activity) : 0)); + activity ? errdetail("Failed process was running: %s", activity) : 0); else if (WIFSIGNALED(exitstatus)) { #if defined(WIN32) @@ -2845,20 +2836,27 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus) /*------ translator: %s is a noun phrase describing a child process, such as "server process" */ - (errmsg("%s (PID %d) was terminated by exception 0x%X", - procname, pid, WTERMSIG(exitstatus)), - errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."), - activity ? errdetail("Failed process was running: %s", activity) : 0)); + addtype ? + errmsg("\"%s\" process of type \"%s\" (PID %d) was terminated by exception 0x%X", + GetBackendTypeDesc(proctype), addtype, pid, WTERMSIG(exitstatus)) : + errmsg("\"%s\" process (PID %d) was terminated by exception 0x%X", + GetBackendTypeDesc(proctype), addtype, pid, WTERMSIG(exitstatus)), + errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."), + activity ? errdetail("Failed process was running: %s", activity) : 0); #else ereport(lev, /*------ translator: %s is a noun phrase describing a child process, such as "server process" */ - (errmsg("%s (PID %d) was terminated by signal %d: %s", - procname, pid, WTERMSIG(exitstatus), - pg_strsignal(WTERMSIG(exitstatus))), - activity ? errdetail("Failed process was running: %s", activity) : 0)); + addtype ? + errmsg("\"%s\" process of type \"%s\" (PID %d) was terminated by signal %d: %s", + GetBackendTypeDesc(proctype), addtype, pid, WTERMSIG(exitstatus), + pg_strsignal(WTERMSIG(exitstatus))) : + errmsg("\"%s\" process (PID %d) was terminated by signal %d: %s", + GetBackendTypeDesc(proctype), pid, WTERMSIG(exitstatus), + pg_strsignal(WTERMSIG(exitstatus))), + activity ? errdetail("Failed process was running: %s", activity) : 0); #endif } else @@ -2867,9 +2865,12 @@ LogChildExit(int lev, const char *procname, int pid, int exitstatus) /*------ translator: %s is a noun phrase describing a child process, such as "server process" */ - (errmsg("%s (PID %d) exited with unrecognized status %d", - procname, pid, exitstatus), - activity ? errdetail("Failed process was running: %s", activity) : 0)); + addtype ? + errmsg("\"%s\" process of type \"%s\" (PID %d) exited with unrecognized status %d", + GetBackendTypeDesc(proctype), addtype, pid, exitstatus) : + errmsg("\"%s\" process (PID %d) exited with unrecognized status %d", + GetBackendTypeDesc(proctype), pid, exitstatus), + activity ? errdetail("Failed process was running: %s", activity) : 0); } /* diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm index 35413f140198..92c5b06868f3 100644 --- a/src/test/perl/PostgreSQL/Test/Cluster.pm +++ b/src/test/perl/PostgreSQL/Test/Cluster.pm @@ -2696,7 +2696,7 @@ sub connect_fails if (defined($params{log_like}) or defined($params{log_unlike})) { $self->wait_for_log( - qr/DEBUG: (?:00000: )?forked new client backend, pid=(\d+) socket.*DEBUG: (?:00000: )?client backend \(PID \1\) exited with exit code \d/s, + qr/DEBUG: (?:00000: )?forked new client backend, pid=(\d+) socket.*DEBUG: (?:00000: )?process of type \"client backend\" \(PID \1\) exited with exit code \d/s, $log_location); $self->log_check($test_name, $log_location, %params); diff --git a/src/test/postmaster/t/002_connection_limits.pl b/src/test/postmaster/t/002_connection_limits.pl index 4a7fb16261f8..a67ae77d59af 100644 --- a/src/test/postmaster/t/002_connection_limits.pl +++ b/src/test/postmaster/t/002_connection_limits.pl @@ -69,7 +69,7 @@ sub connect_fails_wait $node->connect_fails($connstr, $test_name, %params); $node->wait_for_log( - qr/DEBUG: (00000: )?client backend.*exited with exit code 1/, + qr/DEBUG: (00000: )?process of type "client backend".*exited with exit code 1/, $log_location); ok(1, "$test_name: client backend process exited"); }