From 98e63807d9dbbf2d6153ce4b8139a49f84339a07 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Wed, 25 Mar 2026 14:49:12 -0700
Subject: [PATCH v31 2/2] fixup: several changes.

- use opt-out style.
- adjust default values.
- improve comments.
- fixes typos
etc.
---
 src/backend/access/common/reloptions.c        |  2 +-
 src/backend/access/heap/vacuumlazy.c          |  5 +-
 src/backend/commands/vacuumparallel.c         | 52 +++++++++++++------
 src/backend/postmaster/autovacuum.c           | 36 +++++++------
 src/backend/utils/init/globals.c              |  2 +-
 src/backend/utils/misc/guc.c                  |  7 +--
 src/backend/utils/misc/guc_parameters.dat     |  2 +-
 src/backend/utils/misc/postgresql.conf.sample |  2 +-
 .../t/001_parallel_autovacuum.pl              | 22 ++++----
 9 files changed, 82 insertions(+), 48 deletions(-)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ce41b015b32..cee705500f8 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -243,7 +243,7 @@ static relopt_int intRelOpts[] =
 			RELOPT_KIND_HEAP,
 			ShareUpdateExclusiveLock
 		},
-		0, -1, 1024
+		-1, -1, 1024
 	},
 	{
 		{
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 8c7de657976..9fd4f6febbe 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -864,8 +864,11 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 	dead_items_alloc(vacrel, params.nworkers);
 
 #ifdef USE_INJECTION_POINTS
+
 	/*
-	 * Trigger injection point, if parallel autovacuum is about to be started.
+	 * Used by tests to pause before parallel vacuum is launched, allowing
+	 * test code to modify configuration that the leader then propagates to
+	 * workers.
 	 */
 	if (AmAutoVacuumWorkerProcess() && ParallelVacuumIsActive(vacrel))
 		INJECTION_POINT("autovacuum-start-parallel-vacuum", NULL);
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index 62b6f50b538..13544de5b93 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -69,10 +69,12 @@ typedef struct PVSharedCostParams
 {
 	/*
 	 * The generation counter is incremented by the leader process each time
-	 * it updates the shared cost-based vacuum delay parameters. Paralell
+	 * it updates the shared cost-based vacuum delay parameters. Parallel
 	 * vacuum workers compares it with their local generation,
 	 * shared_params_generation_local, to detect whether they need to refresh
-	 * their local parameters.
+	 * their local parameters. The generation starts from 1 so that a freshly
+	 * started worker (whose local copy is 0) will always load the initial
+	 * parameters on its first check.
 	 */
 	pg_atomic_uint32 generation;
 
@@ -158,13 +160,13 @@ typedef struct PVShared
 
 	/*
 	 * If 'true' then we are running parallel autovacuum. Otherwise, we are
-	 * running parallel maintenence VACUUM.
+	 * running parallel maintenance VACUUM.
 	 */
 	bool		is_autovacuum;
 
 	/*
-	 * Struct for syncing cost-based vacuum delay parameters between
-	 * supportive parallel autovacuum workers with leader worker.
+	 * Cost-based vacuum delay parameters shared between the autovacuum leader
+	 * and its parallel workers.
 	 */
 	PVSharedCostParams cost_params;
 } PVShared;
@@ -271,7 +273,13 @@ struct ParallelVacuumState
 
 static PVSharedCostParams *pv_shared_cost_params = NULL;
 
-/* See comments in the PVSharedCostParams for the details */
+/*
+ * Worker-local copy of the last cost-parameter generation this worker has
+ * applied.  Initialized to 0; since the leader initializes the shared
+ * generation counter to 1, the first call to
+ * parallel_vacuum_update_shared_delay_params() will always detect a
+ * mismatch and read the initial parameters from shared memory.
+ */
 static uint32 shared_params_generation_local = 0;
 
 static int	parallel_vacuum_compute_workers(Relation *indrels, int nindexes, int nrequested,
@@ -455,7 +463,7 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
 	if (shared->is_autovacuum)
 	{
 		parallel_vacuum_set_cost_parameters(&shared->cost_params);
-		pg_atomic_init_u32(&shared->cost_params.generation, 0);
+		pg_atomic_init_u32(&shared->cost_params.generation, 1);
 		SpinLockInit(&shared->cost_params.mutex);
 
 		pv_shared_cost_params = &(shared->cost_params);
@@ -623,7 +631,7 @@ parallel_vacuum_set_cost_parameters(PVSharedCostParams *params)
  * Updates the cost-based vacuum delay parameters for parallel autovacuum
  * workers.
  *
- * For non-autovacuum parallel worker this function will have no effect.
+ * For non-autovacuum parallel workers, this function will have no effect.
  */
 void
 parallel_vacuum_update_shared_delay_params(void)
@@ -632,7 +640,7 @@ parallel_vacuum_update_shared_delay_params(void)
 
 	Assert(IsParallelWorker());
 
-	/* Quick return if the wokrer is not running for the autovacuum */
+	/* Quick return if the worker is not running for the autovacuum */
 	if (pv_shared_cost_params == NULL)
 		return;
 
@@ -681,7 +689,7 @@ parallel_vacuum_propagate_shared_delay_params(void)
 		return;
 
 	/*
-	 * Check if any delay parameters has changed. We can read them without
+	 * Check if any delay parameters have changed. We can read them without
 	 * locks as only the leader can modify them.
 	 */
 	if (vacuum_cost_delay == pv_shared_cost_params->cost_delay &&
@@ -905,9 +913,10 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan
 	}
 
 #ifdef USE_INJECTION_POINTS
+
 	/*
-	 * This injection point is used to wait until parallel autovacuum workers
-	 * finishes their part of index processing.
+	 * Used by tests to pause after workers are launched but before index
+	 * vacuuming begins.
 	 */
 	if (nworkers > 0)
 		INJECTION_POINT("autovacuum-leader-before-indexes-processing", NULL);
@@ -1247,15 +1256,26 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
 								shared->dead_items_handle);
 
 	/* Set cost-based vacuum delay */
-	VacuumUpdateCosts();
+	if (shared->is_autovacuum)
+	{
+		/*
+		 * Parallel autovacuum workers initialize cost-based delay parameters
+		 * from the leader's shared state rather than GUC defaults, because
+		 * the leader may have applied per-table or autovacuum-specific
+		 * overrides.  pv_shared_cost_params must be set before calling
+		 * parallel_vacuum_update_shared_delay_params().
+		 */
+		pv_shared_cost_params = &(shared->cost_params);
+		parallel_vacuum_update_shared_delay_params();
+	}
+	else
+		VacuumUpdateCosts();
+
 	VacuumCostBalance = 0;
 	VacuumCostBalanceLocal = 0;
 	VacuumSharedCostBalance = &(shared->cost_balance);
 	VacuumActiveNWorkers = &(shared->active_nworkers);
 
-	if (shared->is_autovacuum)
-		pv_shared_cost_params = &(shared->cost_params);
-
 	/* Set parallel vacuum state */
 	pvs.indrels = indrels;
 	pvs.nindexes = nindexes;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 562514e2ece..ce893db1ab5 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2797,7 +2797,6 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		int			multixact_freeze_table_age;
 		int			log_vacuum_min_duration;
 		int			log_analyze_min_duration;
-		int			nparallel_workers = -1; /* disabled by default */
 
 		/*
 		 * Calculate the vacuum cost parameters and the freeze ages.  If there
@@ -2858,19 +2857,6 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_params.index_cleanup = VACOPTVALUE_UNSPECIFIED;
 		tab->at_params.truncate = VACOPTVALUE_UNSPECIFIED;
 
-		/* Decide whether we need to process indexes of table in parallel. */
-		if (avopts)
-		{
-			if (avopts->autovacuum_parallel_workers > 0)
-				nparallel_workers = avopts->autovacuum_parallel_workers;
-			else if (avopts->autovacuum_parallel_workers == -1)
-			{
-				nparallel_workers = autovacuum_max_parallel_workers > 0
-					? autovacuum_max_parallel_workers
-					: -1; /* disable parallelism if parameter's value is 0 */
-			}
-		}
-
 		tab->at_params.freeze_min_age = freeze_min_age;
 		tab->at_params.freeze_table_age = freeze_table_age;
 		tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
@@ -2879,7 +2865,27 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_params.log_vacuum_min_duration = log_vacuum_min_duration;
 		tab->at_params.log_analyze_min_duration = log_analyze_min_duration;
 		tab->at_params.toast_parent = InvalidOid;
-		tab->at_params.nworkers = nparallel_workers;
+
+		/* Determine the number of parallel vacuum workers to use */
+		tab->at_params.nworkers = 0;
+		if (avopts)
+		{
+			if (avopts->autovacuum_parallel_workers == 0)
+			{
+				/*
+				 * Disable parallel vacuum, if the reloption sets the parallel
+				 * degree as zero.
+				 */
+				tab->at_params.nworkers = -1;
+			}
+			else if (avopts->autovacuum_parallel_workers > 0)
+				tab->at_params.nworkers = avopts->autovacuum_parallel_workers;
+
+			/*
+			 * autovacuum_parallel_workers == -1 falls through, keep
+			 * nworkers=0
+			 */
+		}
 
 		/*
 		 * Later, in vacuum_rel(), we check reloptions for any
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 8265a82b639..24ddb276f0c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -143,7 +143,7 @@ int			NBuffers = 16384;
 int			MaxConnections = 100;
 int			max_worker_processes = 8;
 int			max_parallel_workers = 8;
-int			autovacuum_max_parallel_workers = 2;
+int			autovacuum_max_parallel_workers = 0;
 int			MaxBackends = 0;
 
 /* GUC parameters for vacuum */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 45b39b7c47f..1ac8e8fc3be 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3359,9 +3359,10 @@ set_config_with_handle(const char *name, config_handle *handle,
 	 * Also allow normal setting if the GUC is marked GUC_ALLOW_IN_PARALLEL.
 	 *
 	 * Other changes might need to affect other workers, so forbid them. Note,
-	 * that parallel autovacuum leader is an exception, because only
-	 * cost-based delays need to be affected also to parallel autovacuum
-	 * workers, and we will handle it elsewhere if appropriate.
+	 * that parallel autovacuum leader is an exception because only cost-based
+	 * delays need to be affected also to parallel autovacuum workers. These
+	 * parameters are propagated to its workers during parallel vacuum (see
+	 * vacuumparallel.c for details).
 	 */
 	if (IsInParallelMode() && !AmAutoVacuumWorkerProcess() && changeVal &&
 		action != GUC_ACTION_SAVE &&
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 3d2fd35a004..275198f2023 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -157,7 +157,7 @@
 { name => 'autovacuum_max_parallel_workers', type => 'int', context => 'PGC_SIGHUP', group => 'VACUUM_AUTOVACUUM',
   short_desc => 'Maximum number of parallel workers that can be used by a single autovacuum worker.',
   variable => 'autovacuum_max_parallel_workers',
-  boot_val => '2',
+  boot_val => '0',
   min => '0',
   max => 'MAX_PARALLEL_WORKER_LIMIT',
 },
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 11d96f4dd4f..9853df0bdf7 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -713,7 +713,7 @@
 #autovacuum_worker_slots = 16           # autovacuum worker slots to allocate
                                         # (change requires restart)
 #autovacuum_max_workers = 3             # max number of autovacuum subprocesses
-#autovacuum_max_parallel_workers = 2    # limited by max_parallel_workers
+#autovacuum_max_parallel_workers = 0    # limited by max_parallel_workers
 #autovacuum_naptime = 1min              # time between autovacuum runs
 #autovacuum_vacuum_threshold = 50       # min number of row updates before
                                         # vacuum
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 0364019d5f0..2aca32374a2 100644
--- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
+++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
@@ -12,7 +12,7 @@ if ($ENV{enable_injection_points} ne 'yes')
 
 # Before each test we should disable autovacuum for 'test_autovac' table and
 # generate some dead tuples in it. Returns the current autovacuum_count of
-# the table tset_autovac.
+# the table test_autovac.
 sub prepare_for_next_test
 {
 	my ($node, $test_number) = @_;
@@ -47,16 +47,20 @@ my $psql_out;
 my $node = PostgreSQL::Test::Cluster->new('main');
 $node->init;
 
-# Configure postgres, so it can launch parallel autovacuum workers, log all
-# information we are interested in and autovacuum works frequently
+# Limit to one autovacuum worker and disable autovacuum logging globally
+# (enabled only on the test table) so that log checks below match only
+# activity on the expected table.
 $node->append_conf(
 	'postgresql.conf', qq{
-	max_worker_processes = 20
-	max_parallel_workers = 20
-	autovacuum_max_parallel_workers = 4
-	log_min_messages = debug2
-	autovacuum_naptime = '1s'
-	min_parallel_index_scan_size = 0
+autovacuum_max_workers = 1
+autovacuum_worker_slots = 1
+autovacuum_max_parallel_workers = 2
+max_worker_processes = 10
+max_parallel_workers = 10
+log_min_messages = debug2
+autovacuum_naptime = '1s'
+min_parallel_index_scan_size = 0
+log_autovacuum_min_duration = -1
 });
 $node->start;
 
-- 
2.53.0

