Wake up autovacuum launcher from postmaster when a worker exits
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 15 Jan 2026 16:02:25 +0000 (18:02 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 15 Jan 2026 16:02:25 +0000 (18:02 +0200)
When an autovacuum worker exits, the launcher needs to be notified
with SIGUSR2, so that it can rebalance and possibly launch a new
worker. The launcher must be notified only after the worker has
finished ProcKill(), so that the worker slot is available for a new
worker. Before this commit, the autovacuum worker was responsible for
that, which required a slightly complicated dance to pass the
launcher's PID from FreeWorkerInfo() to ProcKill() in a global
variable.

Simplify that by moving the responsibility of the signaling to the
postmaster. The postmaster was already doing it when it failed to fork
a worker process, so it seems logical to make it responsible for
notifying the launcher on worker exit too. That's also how the
notification on background worker exit is done.

Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: li carol <carol.li2025@outlook.com>
Discussion: https://www.postgresql.org/message-id/a5e27d25-c7e7-45d5-9bac-a17c8f462def@iki.fi

src/backend/postmaster/autovacuum.c
src/backend/postmaster/postmaster.c
src/backend/storage/lmgr/proc.c
src/include/postmaster/autovacuum.h

index 3e507d23cc99c635e2690f2c86b5843440abcb08..22379de1e315afee06b5956dc81715d32b1dbcf7 100644 (file)
@@ -323,9 +323,6 @@ avl_dbase  *avl_dbase_array;
 /* Pointer to my own WorkerInfo, valid on each worker */
 static WorkerInfo MyWorkerInfo = NULL;
 
-/* PID of launcher, valid only in worker while shutting down */
-int            AutovacuumLauncherPid = 0;
-
 static Oid do_start_worker(void);
 static void ProcessAutoVacLauncherInterrupts(void);
 pg_noreturn static void AutoVacLauncherShutdown(void);
@@ -1604,11 +1601,6 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
        do_autovacuum();
    }
 
-   /*
-    * The launcher will be notified of my death in ProcKill, *if* we managed
-    * to get a worker slot at all
-    */
-
    /* All done, go away */
    proc_exit(0);
 }
@@ -1623,20 +1615,6 @@ FreeWorkerInfo(int code, Datum arg)
    {
        LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
 
-       /*
-        * Wake the launcher up so that he can launch a new worker immediately
-        * if required.  We only save the launcher's PID in local memory here;
-        * the actual signal will be sent when the PGPROC is recycled.  Note
-        * that we always do this, so that the launcher can rebalance the cost
-        * limit setting of the remaining workers.
-        *
-        * We somewhat ignore the risk that the launcher changes its PID
-        * between us reading it and the actual kill; we expect ProcKill to be
-        * called shortly after us, and we assume that PIDs are not reused too
-        * quickly after a process exits.
-        */
-       AutovacuumLauncherPid = AutoVacuumShmem->av_launcherpid;
-
        dlist_delete(&MyWorkerInfo->wi_links);
        MyWorkerInfo->wi_dboid = InvalidOid;
        MyWorkerInfo->wi_tableoid = InvalidOid;
index 921d73226d632e311a2acb45d6ef9b874a21d643..d6133bfebc6649d7f74d5eb12b289b21e3c0a4a7 100644 (file)
@@ -2664,6 +2664,14 @@ CleanupBackend(PMChild *bp,
    if (bp_bgworker_notify)
        BackgroundWorkerStopNotifications(bp_pid);
 
+   /*
+    * If it was an autovacuum worker, wake up the launcher so that it can
+    * immediately launch a new worker or rebalance to cost limit setting of
+    * the remaining workers.
+    */
+   if (bp_bkend_type == B_AUTOVAC_WORKER && AutoVacLauncherPMChild != NULL)
+       signal_child(AutoVacLauncherPMChild, SIGUSR2);
+
    /*
     * If it was a background worker, also update its RegisteredBgWorker
     * entry.
index 66274029c74bfeaf85707eb47319927bc42dec06..063826ae57635765531a19600ed449eaedfed036 100644 (file)
@@ -1035,10 +1035,6 @@ ProcKill(int code, Datum arg)
    ProcGlobal->spins_per_delay = update_spins_per_delay(ProcGlobal->spins_per_delay);
 
    SpinLockRelease(ProcStructLock);
-
-   /* wake autovac launcher if needed -- see comments in FreeWorkerInfo */
-   if (AutovacuumLauncherPid != 0)
-       kill(AutovacuumLauncherPid, SIGUSR2);
 }
 
 /*
index e43067d0260842faa36e8709f8de67cd72aa6109..5aa0f3a8ac1a704fdb7ed59687fc0cbd52811f92 100644 (file)
@@ -44,9 +44,6 @@ extern PGDLLIMPORT int autovacuum_multixact_freeze_max_age;
 extern PGDLLIMPORT double autovacuum_vac_cost_delay;
 extern PGDLLIMPORT int autovacuum_vac_cost_limit;
 
-/* autovacuum launcher PID, only valid when worker is shutting down */
-extern PGDLLIMPORT int AutovacuumLauncherPid;
-
 extern PGDLLIMPORT int Log_autovacuum_min_duration;
 extern PGDLLIMPORT int Log_autoanalyze_min_duration;