From a6387345d0e71056442cc08b687f3c5965b8632f Mon Sep 17 00:00:00 2001 From: Nik Samokhvalov Date: Mon, 21 Sep 2026 09:01:50 -0700 Subject: [REPRODUCER] Add reproducer for parallel autovacuum reload wait --- src/backend/commands/vacuumparallel.c | 14 +++ .../t/002_cost_reload_while_waiting.pl | 109 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/test/modules/test_autovacuum/t/002_cost_reload_while_waiting.pl diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index 767d162e57..918508518c 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -41,11 +41,14 @@ #include "commands/progress.h" #include "commands/vacuum.h" #include "executor/instrument.h" +#include "miscadmin.h" #include "optimizer/paths.h" #include "pgstat.h" +#include "postmaster/interrupt.h" #include "storage/bufmgr.h" #include "storage/proc.h" #include "tcop/tcopprot.h" +#include "utils/injection_point.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -929,6 +932,9 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan /* Vacuum the indexes that can be processed by only leader process */ parallel_vacuum_process_unsafe_indexes(pvs); + if (pvs->shared->is_autovacuum) + INJECTION_POINT("parallel-autovacuum-leader-before-index", NULL); + /* * Join as a parallel worker. The leader vacuums alone processes all * parallel-safe indexes in the case where no workers are launched. @@ -944,6 +950,11 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan /* Wait for all vacuum workers to finish */ WaitForParallelWorkersToFinish(pvs->pcxt); + if (pvs->shared->is_autovacuum) + INJECTION_POINT("parallel-autovacuum-leader-after-worker-wait", + ConfigReloadPending ? "reload pending" : + "reload processed"); + for (int i = 0; i < pvs->pcxt->nworkers_launched; i++) InstrAccumParallelQuery(&pvs->buffer_usage[i], &pvs->wal_usage[i]); } @@ -1010,6 +1021,9 @@ parallel_vacuum_process_safe_indexes(ParallelVacuumState *pvs) if (!indstats->parallel_workers_can_process) continue; + if (IsParallelWorker()) + INJECTION_POINT("parallel-autovacuum-worker-before-index", NULL); + /* Do vacuum or cleanup of the index */ parallel_vacuum_process_one_index(pvs, pvs->indrels[idx], indstats); } diff --git a/src/test/modules/test_autovacuum/t/002_cost_reload_while_waiting.pl b/src/test/modules/test_autovacuum/t/002_cost_reload_while_waiting.pl new file mode 100644 index 0000000000..afc3a73367 --- /dev/null +++ b/src/test/modules/test_autovacuum/t/002_cost_reload_while_waiting.pl @@ -0,0 +1,109 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +if ($ENV{enable_injection_points} ne 'yes') +{ + plan skip_all => 'Injection points not supported by this build'; +} + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; +$node->append_conf( + 'postgresql.conf', qq{ +autovacuum_max_workers = 1 +autovacuum_max_parallel_workers = 1 +autovacuum_naptime = '1s' +autovacuum_vacuum_cost_delay = '20ms' +autovacuum_vacuum_cost_limit = 200 +log_min_messages = debug2 +min_parallel_index_scan_size = 0 +}); +$node->start; + +if (!$node->check_extension('injection_points')) +{ + plan skip_all => 'Extension injection_points not installed'; +} + +$node->safe_psql( + 'postgres', q{ + CREATE EXTENSION injection_points; + CREATE TABLE test_autovac (id int, a int) + WITH (autovacuum_enabled = false, + autovacuum_parallel_workers = 1, + autovacuum_vacuum_threshold = 0, + autovacuum_vacuum_scale_factor = 0); + INSERT INTO test_autovac + SELECT g, g FROM generate_series(1, 100) g; + CREATE INDEX test_autovac_id_idx ON test_autovac (id); + CREATE INDEX test_autovac_a_idx ON test_autovac (a); + UPDATE test_autovac SET a = a + 1; + SELECT injection_points_attach( + 'parallel-autovacuum-worker-before-index', 'wait'); + SELECT injection_points_attach( + 'parallel-autovacuum-leader-before-index', 'wait'); + SELECT injection_points_attach( + 'parallel-autovacuum-leader-after-worker-wait', 'notice'); + ALTER TABLE test_autovac SET (autovacuum_enabled = true); +}); + +$node->wait_for_event('autovacuum worker', + 'parallel-autovacuum-leader-before-index'); +$node->wait_for_event('parallel worker', + 'parallel-autovacuum-worker-before-index'); +$node->safe_psql( + 'postgres', q{ + SELECT injection_points_wakeup( + 'parallel-autovacuum-leader-before-index'); + SELECT injection_points_detach( + 'parallel-autovacuum-leader-before-index'); +}); +$node->poll_query_until( + 'postgres', q{ + SELECT EXISTS ( + SELECT 1 + FROM pg_stat_activity + WHERE backend_type = 'autovacuum worker' + AND wait_event = 'ParallelFinish') +}) or die "autovacuum leader did not reach ParallelFinish"; + +my $log_offset = -s $node->logfile; +if (!$ENV{NO_RELOAD_CONTROL}) +{ + $node->safe_psql( + 'postgres', q{ + ALTER SYSTEM SET autovacuum_vacuum_cost_delay = 0; + SELECT pg_reload_conf(); + }); +} +$node->safe_psql( + 'postgres', q{ + SELECT injection_points_wakeup( + 'parallel-autovacuum-worker-before-index'); + SELECT injection_points_detach( + 'parallel-autovacuum-worker-before-index'); +}); + +$node->wait_for_log( + qr/parallel-autovacuum-leader-after-worker-wait \(reload (?:pending|processed)\)/, + $log_offset); + +my $log = slurp_file($node->logfile, $log_offset); +my ($reload_state) = + $log =~ /parallel-autovacuum-leader-after-worker-wait \(reload (pending|processed)\)/; +is($reload_state, 'processed', + 'autovacuum leader processes a configuration reload while waiting'); + +$node->safe_psql( + 'postgres', q{ + SELECT injection_points_detach( + 'parallel-autovacuum-leader-after-worker-wait'); +}); +$node->stop; + +done_testing(); base-commit: b368bdd230181c60e085267fe65d42fd372d3a24 -- 2.50.1 (Apple Git-155)