From ba30e217073cee821cd842f12ed91e4c10adb255 Mon Sep 17 00:00:00 2001
From: Daniil Davidov <d.davydov@postgrespro.ru>
Date: Thu, 15 Jan 2026 23:15:48 +0700
Subject: [PATCH v19 3/5] Cost based parameters propagation for parallel
 autovacuum

---
 src/backend/commands/vacuum.c         | 29 +++++++-
 src/backend/commands/vacuumparallel.c | 99 +++++++++++++++++++++++++++
 src/backend/postmaster/autovacuum.c   |  2 +-
 src/include/commands/vacuum.h         |  2 +
 4 files changed, 129 insertions(+), 3 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index aa4fbec143f..4622107734f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2430,8 +2430,27 @@ vacuum_delay_point(bool is_analyze)
 	/* Always check for interrupts */
 	CHECK_FOR_INTERRUPTS();
 
-	if (InterruptPending ||
-		(!VacuumCostActive && !ConfigReloadPending))
+	if (InterruptPending)
+		return;
+
+	if (!AmAutoVacuumWorkerProcess())
+	{
+		/*
+		 * If we are parallel *autovacuum* worker, check whether related to
+		 * cost-based delay parameters had changed in the leader worker. If
+		 * so, corresponding parameters will be updated to the values which
+		 * leader worker is operating on.
+		 *
+		 * Do it before checking VacuumCostActive, because its value might be
+		 * changed after leader's parameters consumption.
+		 *
+		 * Note, that this function has no effect if we are non-autovacuum
+		 * parallel worker.
+		 */
+		parallel_vacuum_update_shared_delay_params();
+	}
+
+	if (!VacuumCostActive && !ConfigReloadPending)
 		return;
 
 	/*
@@ -2445,6 +2464,12 @@ vacuum_delay_point(bool is_analyze)
 		ConfigReloadPending = false;
 		ProcessConfigFile(PGC_SIGHUP);
 		VacuumUpdateCosts();
+
+		/*
+		 * If we are parallel autovacuum leader and some of cost-based
+		 * parameters had changed, let other parallel workers know.
+		 */
+		parallel_vacuum_propagate_cost_based_params();
 	}
 
 	/*
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index c32314f9731..71449630b63 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -53,6 +53,25 @@
 #define PARALLEL_VACUUM_KEY_WAL_USAGE		4
 #define PARALLEL_VACUUM_KEY_INDEX_STATS		5
 
+/*
+ * Only autovacuum leader can reload config file. We use this structure in
+ * parallel autovacuum for keeping worker's parameters in sync with leader's
+ * parameters.
+ */
+typedef struct PVSharedCostParams
+{
+	slock_t		spinlock;		/* protects all fields below */
+
+	/* Copies of corresponding parameters from autovacuum leader process */
+	double		cost_delay;
+	int			cost_limit;
+	int			cost_page_dirty;
+	int			cost_page_hit;
+	int			cost_page_miss;
+}			PVSharedCostParams;
+
+static PVSharedCostParams * pv_shared_cost_params = NULL;
+
 /*
  * Shared information among parallel workers.  So this is allocated in the DSM
  * segment.
@@ -122,6 +141,18 @@ typedef struct PVShared
 
 	/* Statistics of shared dead items */
 	VacDeadItemsInfo dead_items_info;
+
+	/*
+	 * If 'true' then we are running parallel autovacuum. Otherwise, we are
+	 * running parallel maintenence VACUUM.
+	 */
+	bool		am_parallel_autovacuum;
+
+	/*
+	 * Struct for syncing parameters between supportive parallel autovacuum
+	 * workers with leader worker.
+	 */
+	PVSharedCostParams cost_params;
 } PVShared;
 
 /* Status used during parallel index vacuum or cleanup */
@@ -395,6 +426,19 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
 	pg_atomic_init_u32(&(shared->active_nworkers), 0);
 	pg_atomic_init_u32(&(shared->idx), 0);
 
+	shared->am_parallel_autovacuum = AmAutoVacuumWorkerProcess();
+
+	if (shared->am_parallel_autovacuum)
+	{
+		shared->cost_params.cost_delay = vacuum_cost_delay;
+		shared->cost_params.cost_limit = vacuum_cost_limit;
+		shared->cost_params.cost_page_dirty = VacuumCostPageDirty;
+		shared->cost_params.cost_page_hit = VacuumCostPageHit;
+		shared->cost_params.cost_page_miss = VacuumCostPageMiss;
+		SpinLockInit(&shared->cost_params.spinlock);
+		pv_shared_cost_params = &(shared->cost_params);
+	}
+
 	shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_SHARED, shared);
 	pvs->shared = shared;
 
@@ -537,6 +581,58 @@ parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs, long num_table_tup
 	parallel_vacuum_process_all_indexes(pvs, num_index_scans, false, wusage);
 }
 
+/*
+ * Function to be called from parallel autovacuum worker in order to sync
+ * some cost-based delay parameter with the leader worker.
+ */
+bool
+parallel_vacuum_update_shared_delay_params(void)
+{
+	/* Check whether we are running parallel autovacuum */
+	if (pv_shared_cost_params == NULL)
+		return false;
+
+	Assert(IsParallelWorker() && !AmAutoVacuumWorkerProcess());
+
+	SpinLockAcquire(&pv_shared_cost_params->spinlock);
+
+	VacuumCostDelay = pv_shared_cost_params->cost_delay;
+	VacuumCostLimit = pv_shared_cost_params->cost_limit;
+	VacuumCostPageDirty = pv_shared_cost_params->cost_page_dirty;
+	VacuumCostPageHit = pv_shared_cost_params->cost_page_hit;
+	VacuumCostPageMiss = pv_shared_cost_params->cost_page_miss;
+
+	SpinLockRelease(&pv_shared_cost_params->spinlock);
+
+	VacuumUpdateCosts();
+
+	return true;
+}
+
+/*
+ * Function to be called from parallel autovacuum leader in order to propagate
+ * some cost-based parameters to the supportive workers.
+ */
+void
+parallel_vacuum_propagate_cost_based_params(void)
+{
+	/* Check whether we are running parallel autovacuum */
+	if (pv_shared_cost_params == NULL)
+		return;
+
+	Assert(AmAutoVacuumWorkerProcess());
+
+	SpinLockAcquire(&pv_shared_cost_params->spinlock);
+
+	pv_shared_cost_params->cost_delay = vacuum_cost_delay;
+	pv_shared_cost_params->cost_limit = vacuum_cost_limit;
+	pv_shared_cost_params->cost_page_dirty = VacuumCostPageDirty;
+	pv_shared_cost_params->cost_page_hit = VacuumCostPageHit;
+	pv_shared_cost_params->cost_page_miss = VacuumCostPageMiss;
+
+	SpinLockRelease(&pv_shared_cost_params->spinlock);
+}
+
 /*
  * Compute the number of parallel worker processes to request.  Both index
  * vacuum and index cleanup can be executed with parallel workers.
@@ -1094,6 +1190,9 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
 	VacuumSharedCostBalance = &(shared->cost_balance);
 	VacuumActiveNWorkers = &(shared->active_nworkers);
 
+	if (shared->am_parallel_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 097b1dd55cf..98965fd8e2d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1693,7 +1693,7 @@ VacuumUpdateCosts(void)
 	}
 	else
 	{
-		/* Must be explicit VACUUM or ANALYZE */
+		/* Must be explicit VACUUM or ANALYZE or parallel autovacuum worker */
 		vacuum_cost_delay = VacuumCostDelay;
 		vacuum_cost_limit = VacuumCostLimit;
 	}
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index ec5d70aacdc..09696a8eafe 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -411,6 +411,8 @@ extern void parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs,
 												int num_index_scans,
 												bool estimated_count,
 												PVWorkersUsage *wusage);
+extern bool parallel_vacuum_update_shared_delay_params(void);
+extern void parallel_vacuum_propagate_cost_based_params(void);
 extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc);
 
 /* in commands/analyze.c */
-- 
2.43.0

