From 6f37d18f2145ccd86f5deb4daf94f0ba6536517f Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Wed, 23 Sep 2026 22:02:08 -0700 Subject: [PATCH] Refresh autovacuum cost parameters while waiting for parallel workers An autovacuum leader publishes its cost-based delay parameters to its parallel vacuum workers at its cost delay points. Once it has finished its own share of the indexes and is only waiting for its workers, it has no delay points left, so it stops publishing for as long as that wait lasts, which can be as long as the largest index takes. Two kinds of changes were lost for that whole time. 1. A config reload. The signal wakes the leader, but the wait does not act on it, so the reload stays pending until the wait ends. 2. A change in the number of autovacuum workers sharing the cost limit. Nothing signals that at all, and a waiting leader never re-reads the count because it no longer naps. Fix the first by refreshing and publishing the parameters from the wait itself, on every wakeup, when the process is an autovacuum worker. Fix the second by waking the workers that share the limit whenever the count changes, so a waiting leader picks it up the same way. A worker that is busy vacuuming ignores the extra wakeup. Reported-by: Nikolay Samokhvalov Discussion: https://postgr.es/m/CAM527d-GL%3DJp2EJXBSnVBGPK-4XEZwWof5Cv8P0hghS_og6oAg%40mail.gmail.com --- src/backend/access/transam/parallel.c | 8 ++++++ src/backend/commands/vacuumparallel.c | 40 +++++++++++++++++++++++++++ src/backend/postmaster/autovacuum.c | 19 +++++++++++++ src/include/commands/vacuum.h | 1 + 4 files changed, 68 insertions(+) diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index e1806a9a28a..89e45bdeb9c 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -812,6 +812,14 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt) */ CHECK_FOR_INTERRUPTS(); + /* + * An autovacuum leader publishes cost parameter changes to its + * parallel workers at its cost delay points, which it no longer + * reaches while waiting here. Do it here instead, on every wakeup. + */ + if (AmAutoVacuumWorkerProcess()) + parallel_vacuum_refresh_cost_params(); + for (i = 0; i < pcxt->nworkers_launched; ++i) { /* diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index 767d162e578..b2e87bb9433 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -43,6 +43,7 @@ #include "executor/instrument.h" #include "optimizer/paths.h" #include "pgstat.h" +#include "postmaster/interrupt.h" #include "storage/bufmgr.h" #include "storage/proc.h" #include "tcop/tcopprot.h" @@ -725,6 +726,45 @@ parallel_vacuum_propagate_shared_delay_params(void) pg_atomic_fetch_add_u32(&pv_shared_cost_params->generation, 1); } +/* + * Refresh the leader's cost-based vacuum delay parameters and propagate them + * to its parallel vacuum workers. + * + * The leader normally does this at its cost delay points, which it no longer + * reaches once it is only waiting for its workers to finish. It calls this + * from that wait instead, on every wakeup, to pick up a config reload and a + * change in the number of autovacuum workers sharing the cost limit. Both of + * those wake the leader, the first by signal and the second when the count is + * recalculated. + */ +void +parallel_vacuum_refresh_cost_params(void) +{ + Assert(AmAutoVacuumWorkerProcess()); + + /* + * Quick return if the leader process is not sharing the delay parameters. + */ + if (pv_shared_cost_params == NULL) + return; + + if (ConfigReloadPending) + { + ConfigReloadPending = false; + ProcessConfigFile(PGC_SIGHUP); + + /* This rebalances the cost limit too */ + VacuumUpdateCosts(); + } + else + { + /* The number of workers sharing the cost limit may have changed */ + AutoVacuumUpdateCostLimit(); + } + + parallel_vacuum_propagate_shared_delay_params(); +} + /* * Compute the number of parallel worker processes to request. Both index * vacuum and index cleanup can be executed with parallel workers. diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 60ebe828900..d45f361ac0d 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -1814,8 +1814,27 @@ autovac_recalculate_workers_for_balance(void) } if (nworkers_for_balance != orig_nworkers_for_balance) + { pg_atomic_write_u32(&AutoVacuumShmem->av_nworkersForBalance, nworkers_for_balance); + + /* + * Wake up the workers that share the limit. One that is vacuuming + * picks up the new count on its next nap, but one that is only + * waiting for its parallel vacuum workers never naps, and nothing + * else wakes it while they keep running at the old share. + */ + dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers) + { + WorkerInfo worker = dlist_container(WorkerInfoData, wi_links, iter.cur); + + if (worker->wi_proc == NULL || + pg_atomic_unlocked_test_flag(&worker->wi_dobalance)) + continue; + + SetLatch(&worker->wi_proc->procLatch); + } + } } /* diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 6e3c912bf5c..89fa1fcd261 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -432,6 +432,7 @@ extern void parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs, PVWorkerStats *wstats); extern void parallel_vacuum_update_shared_delay_params(void); extern void parallel_vacuum_propagate_shared_delay_params(void); +extern void parallel_vacuum_refresh_cost_params(void); extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc); /* in commands/analyze.c */ base-commit: 4545cee303c257e58195e3d033c05bf38e2cd4d6 -- 2.43.0