diff --git a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl --- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl +++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl @@ -8,6 +8,7 @@ use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; +use Time::HiRes qw(usleep); if ($ENV{enable_injection_points} ne 'yes') { @@ -395,5 +396,78 @@ $node->safe_psql('postgres', "SELECT injection_points_detach('autovacuum-worker-cost-balanced')"); + +# Test 6 (added for review): +# The reverse of test 5: a worker LEAVES the balance while the leader waits. +# That rebalance takes another path: the leaving worker sets AutoVacRebalance +# in FreeWorkerInfo(), and the launcher recalculates the count. + +$node->poll_query_until( + 'postgres', q{ + SELECT count(*) = 0 FROM pg_stat_activity + WHERE backend_type = 'autovacuum worker' AND datname = 'regress_db2' +}) or die "second autovacuum worker from test 5 did not finish"; + +prepare_for_next_test($node, 6); +$node->safe_psql('regress_db2', + 'ALTER TABLE filler SET (autovacuum_enabled = false)'); +$node->safe_psql('regress_db2', 'UPDATE filler SET id = id + 1 WHERE true'); +$log_offset = -s $node->logfile; + +start_leader_waiting_for_worker($node); + +# A second worker joins and is held, as in test 5: the leader goes to 400. +$node->safe_psql('postgres', + "SELECT injection_points_attach('autovacuum-worker-cost-balanced', 'wait')" +); +$node->safe_psql('regress_db2', + 'ALTER TABLE filler SET (autovacuum_enabled = true)'); +$node->wait_for_log( + qr/Autovacuum VacuumUpdateCosts\(db=$postgresoid, rel=$testautovacoid, dobalance=yes, cost_limit=400,/, + $log_offset); + +# Now let the second worker finish and leave, with the leader still waiting. +my $leave_offset = -s $node->logfile; +$node->safe_psql('postgres', + "SELECT injection_points_wakeup('autovacuum-worker-cost-balanced')"); +$node->safe_psql('postgres', + "SELECT injection_points_detach('autovacuum-worker-cost-balanced')"); +$node->poll_query_until( + 'postgres', q{ + SELECT count(*) = 0 FROM pg_stat_activity + WHERE backend_type = 'autovacuum worker' AND datname = 'regress_db2' +}) or die "second autovacuum worker did not finish"; +my $left_at = time; + +# The waiting leader should go back to the whole limit. Poll the log for up +# to 30 s instead of the default timeout. +my $seen = 0; +for (my $i = 0; $i < 300; $i++) +{ + if (slurp_file($node->logfile, $leave_offset) =~ + /Autovacuum VacuumUpdateCosts\(db=$postgresoid, rel=$testautovacoid, dobalance=yes, cost_limit=800,/) + { + $seen = 1; + last; + } + usleep(100_000); +} +my $still_waiting = $node->safe_psql( + 'postgres', q{ + SELECT count(*) FROM pg_stat_activity + WHERE backend_type = 'autovacuum worker' AND wait_event = 'ParallelFinish'}); +note("leader still in ParallelFinish when checked: $still_waiting; waited " + . (time - $left_at) . " s"); +ok($seen, 'waiting leader returns to the whole limit when a worker leaves'); + +release_worker($node); +$node->wait_for_log( + qr/automatic vacuum of table "postgres\.public\.test_autovac"/, + $leave_offset); +my @limits6 = + slurp_file($node->logfile, $leave_offset) =~ + /parallel autovacuum worker updated cost params: cost_limit=(\d+),/g; +note("parallel worker cost_limit sequence after the worker left: @limits6"); +is($limits6[-1] // 'none', '800', 'parallel worker ends at the whole limit'); $node->stop; done_testing();