From 07f7cd5166fde9409ef7dffb788012a894b820f9 Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Fri, 24 Jul 2026 08:18:54 +0000 Subject: [PATCH v2] Propagate rebalanced cost limit to parallel vacuum workers AutoVacuumUpdateCostLimit() runs after each nap in vacuum_delay_point() and follows av_nworkersForBalance, but the new limit never reached the shared cost params in the vacuum DSM: propagation only happened on config reload. Parallel workers computed their delays from the stale limit, so a parallel autovacuum could run at up to twice the configured budget (or half of it) until the next SIGHUP, contradicting the propagation promise in maintenance.sgml. Call parallel_vacuum_propagate_shared_delay_params() after rebalancing. Gated on the leader: parallel workers take the same nap path and must not overwrite the shared parameters. Add a test: pause the leader at the existing injection point, start a second autovacuum worker, check the first parameter load of the parallel workers reports the balanced limit. --- src/backend/commands/vacuum.c | 3 + .../t/001_parallel_autovacuum.pl | 70 ++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..c17aecce7ad 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -2577,6 +2577,9 @@ vacuum_delay_point(bool is_analyze) */ AutoVacuumUpdateCostLimit(); + if (AmAutoVacuumWorkerProcess()) + parallel_vacuum_propagate_shared_delay_params(); + /* Might have gotten an interrupt while sleeping */ CHECK_FOR_INTERRUPTS(); } diff --git a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl index 22f40cb1d50..c199337d449 100644 --- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl +++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl @@ -36,7 +36,7 @@ $node->init; $node->append_conf( 'postgresql.conf', qq{ autovacuum_max_workers = 1 -autovacuum_worker_slots = 1 +autovacuum_worker_slots = 2 autovacuum_max_parallel_workers = 2 max_worker_processes = 10 max_parallel_workers = 10 @@ -167,5 +167,73 @@ ok(1, "vacuum delay parameter changes are propagated to parallel vacuum workers" ); +# Test 3: +# Check whether a cost limit rebalance reaches the parallel workers. The +# leader pauses right after taking the shared cost param snapshot +# (balance = 1, limit 500), then a second autovacuum worker starts +# (balance = 2, limit 250). After resume, the parallel workers' first +# parameter load must show the rebalanced 250, not the snapshotted 500. + +# Second worker's table lives in another database: no indexes, no cost +# reloptions (participates in balancing) +$node->safe_psql('postgres', 'CREATE DATABASE regress_db2'); +$node->safe_psql( + 'regress_db2', qq{ + CREATE TABLE filler (id int, pad text) WITH (autovacuum_enabled = false); + INSERT INTO filler SELECT g, repeat('x', 100) FROM generate_series(1, 200000) g; +}); + +# Allow a second autovacuum worker. +$node->safe_psql( + 'postgres', qq{ + ALTER SYSTEM SET autovacuum_max_workers = 2; + SELECT pg_reload_conf(); +}); + +# Quiesce catalogs so no extra worker skews the balance. +$node->safe_psql($_, 'VACUUM ANALYZE') + for ('postgres', 'regress_db2', 'template1'); + +prepare_for_next_test($node, 3); +$node->safe_psql('regress_db2', 'UPDATE filler SET id = id + 1'); + +my $db2oid = $node->safe_psql('postgres', + "SELECT oid FROM pg_database WHERE datname = 'regress_db2'"); +my $filleroid = + $node->safe_psql('regress_db2', "SELECT 'filler'::regclass::oid"); + +$log_offset = -s $node->logfile; + +# Pause the leader after the shared cost param snapshot. +$node->safe_psql('postgres', + "SELECT injection_points_attach('autovacuum-start-parallel-vacuum', 'wait')" +); +$node->safe_psql('postgres', + 'ALTER TABLE test_autovac SET (autovacuum_enabled = true)'); +$node->wait_for_event('autovacuum worker', + 'autovacuum-start-parallel-vacuum'); + +# Second worker -> balance = 2 +$node->safe_psql('regress_db2', + 'ALTER TABLE filler SET (autovacuum_enabled = true)'); +$node->wait_for_log( + qr/VacuumUpdateCosts\(db=$db2oid, rel=$filleroid, dobalance=yes, cost_limit=250,/, + $log_offset); + +$node->safe_psql('postgres', + "SELECT injection_points_wakeup('autovacuum-start-parallel-vacuum')"); +$node->safe_psql('postgres', + "SELECT injection_points_detach('autovacuum-start-parallel-vacuum')"); + +# First param load must show the rebalanced limit. +$node->wait_for_log( + qr/parallel autovacuum worker updated cost params: cost_limit=\d+,/, + $log_offset); +my $log = slurp_file($node->logfile, $log_offset); +my @limits = + $log =~ /parallel autovacuum worker updated cost params: cost_limit=(\d+),/g; +note("parallel worker cost_limit sequence: @limits"); +is($limits[0], '250', 'parallel workers see the rebalanced cost limit'); + $node->stop; done_testing(); -- 2.55.0