diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index 70364c0292e..8e33e9d8cbb 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -734,6 +734,12 @@ parallel_vacuum_propagate_shared_delay_params(void) * know that they should re-read shared cost params. */ pg_atomic_fetch_add_u32(&pv_shared_cost_params->generation, 1); + + elog(DEBUG2, + "parallel autovacuum leader propagated cost params: cost_limit=%d, cost_delay=%g, cost_page_miss=%d, cost_page_dirty=%d, cost_page_hit=%d, track_cost_delay_timing=%s", + vacuum_cost_limit, vacuum_cost_delay, + VacuumCostPageMiss, VacuumCostPageDirty, VacuumCostPageHit, + track_cost_delay_timing ? "yes" : "no"); } /* @@ -768,8 +774,6 @@ parallel_vacuum_refresh_cost_params(void) } parallel_vacuum_propagate_shared_delay_params(); - - INJECTION_POINT("parallel-autovacuum-leader-cost-updated", NULL); } /* @@ -860,7 +864,6 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan { int nworkers; PVIndVacStatus new_status; - bool leader_participates = true; Assert(!IsParallelWorker()); @@ -974,17 +977,6 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan pvs->pcxt->nworkers_launched, nworkers))); } -#ifdef USE_INJECTION_POINTS - - /* - * Used by tests to leave all parallel-safe indexes to the parallel - * workers, so that the leader waits for them to finish. - */ - if (nworkers > 0 && pvs->pcxt->nworkers_launched > 0 && - IS_INJECTION_POINT_ATTACHED("parallel-vacuum-leader-skip-safe-indexes")) - leader_participates = false; -#endif - /* Vacuum the indexes that can be processed by only leader process */ parallel_vacuum_process_unsafe_indexes(pvs); @@ -992,8 +984,7 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan * Join as a parallel worker. The leader vacuums alone processes all * parallel-safe indexes in the case where no workers are launched. */ - if (leader_participates) - parallel_vacuum_process_safe_indexes(pvs); + parallel_vacuum_process_safe_indexes(pvs); /* * Next, accumulate buffer and WAL usage. (This must wait for the workers @@ -1070,11 +1061,6 @@ parallel_vacuum_process_safe_indexes(ParallelVacuumState *pvs) if (!indstats->parallel_workers_can_process) continue; -#ifdef USE_INJECTION_POINTS - if (IsParallelWorker()) - INJECTION_POINT("parallel-vacuum-worker-before-index", NULL); -#endif - /* Do vacuum or cleanup of the index */ parallel_vacuum_process_one_index(pvs, pvs->indrels[idx], indstats); } @@ -1310,6 +1296,8 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc) elog(DEBUG1, "starting parallel vacuum worker"); + INJECTION_POINT("parallel-vacuum-worker-start", NULL); + shared = (PVShared *) shm_toc_lookup(toc, PARALLEL_VACUUM_KEY_SHARED, false); /* Set debug_query_string for individual workers */ 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 39960f6be74..a16df331fc6 100644 --- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl +++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl @@ -263,50 +263,9 @@ ok( $node->poll_query_until( }), 'second autovacuum worker finished'); -# Start autovacuum on test_autovac with its parallel worker held before its -# first index, and the leader leaving all indexes to the parallel worker. -# Returns once the leader waits for the parallel worker to finish. -sub start_leader_waiting -{ - my ($node) = @_; - - $node->safe_psql( - 'postgres', q{ - SELECT injection_points_attach('parallel-vacuum-worker-before-index', 'wait'); - SELECT injection_points_attach('parallel-vacuum-leader-skip-safe-indexes', 'notice'); - ALTER TABLE test_autovac SET (autovacuum_enabled = true); - }); - $node->wait_for_event('parallel worker', - 'parallel-vacuum-worker-before-index'); - ok( $node->poll_query_until( - 'postgres', q{ - SELECT count(*) > 0 FROM pg_stat_activity - WHERE backend_type = 'autovacuum worker' - AND wait_event = 'ParallelFinish' - }), - 'autovacuum leader waits for its parallel worker'); -} - -# Release the parallel worker held by start_leader_waiting(). -sub release_parallel_worker -{ - my ($node) = @_; - - $node->safe_psql( - 'postgres', q{ - SELECT injection_points_detach('parallel-vacuum-leader-skip-safe-indexes'); - SELECT injection_points_detach('parallel-vacuum-worker-before-index'); - SELECT injection_points_wakeup('parallel-vacuum-worker-before-index'); - }); -} - # Test 4: # Check whether a config reload is serviced while the autovacuum leader waits # for its parallel worker. -my $postgresoid = $node->safe_psql('postgres', - "SELECT oid FROM pg_database WHERE datname = 'postgres'"); -my $testautovacid = - $node->safe_psql('postgres', "SELECT 'test_autovac'::regclass::oid"); $node->safe_psql( 'postgres', qq{ @@ -319,8 +278,19 @@ $node->safe_psql( prepare_for_next_test($node, 4); $log_offset = -s $node->logfile; -start_leader_waiting($node); +# Let an autovacuum worker process test_autovac with its parallel worker held +# before the parallel worker reads the cost-based delay parameters. The +# leader then processes all indexes by itself and waits for the parallel +# worker to finish. Change the parameters only once the leader is waiting, +# or it would pick up the change before reaching the code path under test. +$node->safe_psql( + 'postgres', q{ +SELECT injection_points_attach('parallel-vacuum-worker-start', 'wait'); +ALTER TABLE test_autovac SET (autovacuum_enabled = true); +}); +$node->wait_for_event('autovacuum worker', 'ParallelFinish'); +# Update cost-based delay parameters. $node->safe_psql( 'postgres', qq{ ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 800; @@ -331,15 +301,26 @@ $node->safe_psql( SELECT pg_reload_conf(); }); -# The leader must process the reload before the parallel worker is released. +# The parallel worker reads the parameters only once, when it starts, since +# the leader has already processed all indexes. So the leader must have +# propagated the new parameters before the parallel worker is released. $node->wait_for_log( - qr/Autovacuum VacuumUpdateCosts\(db=$postgresoid, rel=$testautovacid, dobalance=yes, cost_limit=800, cost_delay=8 /, + qr/parallel autovacuum leader propagated cost params: cost_limit=800,/, $log_offset); -release_parallel_worker($node); +# Release the parallel worker. It reads the cost-based delay parameters the +# leader has propagated as soon as it resumes. +$node->safe_psql( + 'postgres', + q{ +SELECT injection_points_detach('parallel-vacuum-worker-start'); +SELECT injection_points_wakeup('parallel-vacuum-worker-start'); +}); $node->wait_for_log( qr/parallel autovacuum worker updated cost params: cost_limit=800, cost_delay=8, cost_page_miss=11, cost_page_dirty=12, cost_page_hit=13/, $log_offset); + +# Wait for the autovacuum on test_autovac to finish. $node->wait_for_log( qr/automatic vacuum of table "postgres\.public\.test_autovac"/, $log_offset); @@ -363,47 +344,63 @@ $node->safe_psql('regress_db2', 'UPDATE filler SET id = id + 1'); $log_offset = -s $node->logfile; -start_leader_waiting($node); +# As in Test 4, hold the parallel worker and wait for the leader to process +# all indexes and wait for the parallel worker to finish. +$node->safe_psql( + 'postgres', q{ +SELECT injection_points_attach('parallel-vacuum-worker-start', 'wait'); +ALTER TABLE test_autovac SET (autovacuum_enabled = true); +}); +$node->wait_for_event('autovacuum worker', 'ParallelFinish'); -# Hold the second worker, so that the balance stays at 2. Attach the notice -# before the rebalance, as the rebalance wakeup is the only thing that brings -# the waiting leader to this point. +# Hold the second worker, so that the number of autovacuum workers sharing +# the cost limit stays at 2 until the parallel worker has read the +# parameters. Once the second worker finishes, the leader would propagate +# the original cost limit again. $node->safe_psql( 'postgres', q{ - SELECT injection_points_attach('autovacuum-worker-cost-balanced', 'wait'); - SELECT injection_points_attach('parallel-autovacuum-leader-cost-updated', 'notice'); + SELECT injection_points_attach('autovacuum-worker-cost-balanced', 'wait'); }); $node->safe_psql('regress_db2', 'ALTER TABLE filler SET (autovacuum_enabled = true)'); + +# Wait for the second worker to update its cost parameters. It has +# recalculated the number of workers sharing the cost limit, now 2, and woken +# up the leader. $node->wait_for_log( qr/VacuumUpdateCosts\(db=$db2oid, rel=$filleroid, dobalance=yes, cost_limit=300,/, $log_offset); + +# Likewise, the leader must propagate the rebalanced cost limit before the +# parallel worker is released. $node->wait_for_log( - qr/notice triggered for injection point parallel-autovacuum-leader-cost-updated/, + qr/parallel autovacuum leader propagated cost params: cost_limit=300,/, $log_offset); -$node->safe_psql('postgres', - "SELECT injection_points_detach('parallel-autovacuum-leader-cost-updated')" -); -release_parallel_worker($node); +# Release the parallel worker. It reads the cost-based delay parameters the +# leader has propagated as soon as it resumes. +$node->safe_psql( + 'postgres', + q{ +SELECT injection_points_detach('parallel-vacuum-worker-start'); +SELECT injection_points_wakeup('parallel-vacuum-worker-start'); +}); $node->wait_for_log( qr/parallel autovacuum worker updated cost params: cost_limit=300,/, $log_offset); +# Release the second worker. $node->safe_psql( - 'postgres', q{ - SELECT injection_points_wakeup('autovacuum-worker-cost-balanced'); - SELECT injection_points_detach('autovacuum-worker-cost-balanced'); + 'postgres', + q{ +SELECT injection_points_detach('autovacuum-worker-cost-balanced'); +SELECT injection_points_wakeup('autovacuum-worker-cost-balanced'); }); + +# Wait for the autovacuum on test_autovac to finish. $node->wait_for_log( qr/automatic vacuum of table "postgres\.public\.test_autovac"/, $log_offset); -ok( $node->poll_query_until( - 'postgres', q{ - SELECT count(*) = 0 FROM pg_stat_activity - WHERE backend_type = 'autovacuum worker' AND datname = 'regress_db2' - }), - 'second autovacuum worker finished'); ok(1, "cost rebalance is propagated while the leader waits for workers"); $node->stop;