From 5e2b470db102546c3124da1eef8acc42d5c2fead Mon Sep 17 00:00:00 2001
From: Daniil Davidov <d.davydov@postgrespro.ru>
Date: Sat, 7 Feb 2026 11:07:44 +0700
Subject: [PATCH 5/7] fixes for patch 3

---
 src/backend/commands/vacuum.c         |  14 +--
 src/backend/commands/vacuumparallel.c | 120 ++++++++++++++++----------
 src/include/commands/vacuum.h         |   2 +-
 src/tools/pgindent/typedefs.list      |   2 +
 4 files changed, 82 insertions(+), 56 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 9847ed1c2da..70882544d05 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -2433,19 +2433,13 @@ vacuum_delay_point(bool is_analyze)
 	if (InterruptPending)
 		return;
 
-	if (!AmAutoVacuumWorkerProcess() && IsParallelWorker())
+	if (IsParallelWorker())
 	{
 		/*
-		 * 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.
+		 * Possibly update cost-based delay parameters.
 		 *
 		 * 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.
+		 * changed after calling this function.
 		 */
 		parallel_vacuum_update_shared_delay_params();
 	}
@@ -2469,7 +2463,7 @@ vacuum_delay_point(bool is_analyze)
 		 * 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();
+		parallel_vacuum_propagate_shared_delay_params();
 	}
 
 	/*
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index 640173eada8..ccb3812165c 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -54,9 +54,35 @@
 #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.
+ * Helper for the PVSharedCostParams structure (see below), to avoid
+ * repetition.
+ */
+typedef struct CostParamsData
+{
+	double		cost_delay;
+	int			cost_limit;
+	int			cost_page_dirty;
+	int			cost_page_hit;
+	int			cost_page_miss;
+} CostParamsData;
+
+#define	FillCostParamsData(cost_params) \
+	(cost_params)->cost_delay = vacuum_cost_delay; \
+	(cost_params)->cost_limit = vacuum_cost_limit; \
+	(cost_params)->cost_page_dirty = VacuumCostPageDirty; \
+	(cost_params)->cost_page_hit = VacuumCostPageHit; \
+	(cost_params)->cost_page_miss = VacuumCostPageMiss
+
+#define CostParamsDataEqual(params_1, params_2) \
+	((params_1).cost_delay == (params_2).cost_delay && \
+	 (params_1).cost_limit == (params_2).cost_limit && \
+	 (params_1).cost_page_dirty == (params_2).cost_page_dirty && \
+	 (params_1).cost_page_hit == (params_2).cost_page_hit && \
+	 (params_1).cost_page_miss == (params_2).cost_page_miss)
+
+/*
+ * Struct for cost-based vacuum delay related parameters to share among an
+ * autovacuum worker and its parallel vacuum workers.
  */
 typedef struct PVSharedCostParams
 {
@@ -71,20 +97,11 @@ typedef struct PVSharedCostParams
 	 */
 	pg_atomic_uint32 generation;
 
-	slock_t		spinlock;		/* protects all fields below */
+	slock_t		mutex;			/* 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;
-
-/* See comments for structure above for the explanation. */
-static uint32 shared_params_generation_local = 0;
+	CostParamsData params_data;
+} PVSharedCostParams;
 
 /*
  * Shared information among parallel workers.  So this is allocated in the DSM
@@ -269,6 +286,11 @@ struct ParallelVacuumState
 	PVIndVacStatus status;
 };
 
+static PVSharedCostParams *pv_shared_cost_params = NULL;
+
+/* See comments for the PVSharedCostParams structure for the explanation. */
+static uint32 shared_params_generation_local = 0;
+
 static int	parallel_vacuum_compute_workers(Relation *indrels, int nindexes, int nrequested,
 											bool *will_parallel_vacuum);
 static void parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scans,
@@ -444,13 +466,10 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
 
 	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;
+		FillCostParamsData(&shared->cost_params.params_data);
 		pg_atomic_init_u32(&shared->cost_params.generation, 0);
-		SpinLockInit(&shared->cost_params.spinlock);
+		SpinLockInit(&shared->cost_params.mutex);
+
 		pv_shared_cost_params = &(shared->cost_params);
 	}
 
@@ -599,13 +618,18 @@ parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs, long num_table_tup
 }
 
 /*
- * Function to be called from parallel autovacuum worker in order to sync
- * some cost-based delay parameter with the leader worker.
+ * 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.
+ *
+ * For non-autovacuum parallel worker this function will have no effect.
  */
 void
 parallel_vacuum_update_shared_delay_params(void)
 {
-	uint32	params_generation;
+	uint32		params_generation;
+	CostParamsData shared_params_data;
 
 	Assert(IsParallelWorker());
 
@@ -613,22 +637,24 @@ parallel_vacuum_update_shared_delay_params(void)
 	if (pv_shared_cost_params == NULL)
 		return;
 
-	params_generation =	pg_atomic_read_u32(&pv_shared_cost_params->generation);
+	params_generation = pg_atomic_read_u32(&pv_shared_cost_params->generation);
 	Assert(shared_params_generation_local <= params_generation);
 
 	/* Return if parameters had not changed in the leader */
 	if (params_generation == shared_params_generation_local)
 		return;
 
-	SpinLockAcquire(&pv_shared_cost_params->spinlock);
+	SpinLockAcquire(&pv_shared_cost_params->mutex);
 
-	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;
+	shared_params_data = pv_shared_cost_params->params_data;
 
-	SpinLockRelease(&pv_shared_cost_params->spinlock);
+	VacuumCostDelay = shared_params_data.cost_delay;
+	VacuumCostLimit = shared_params_data.cost_limit;
+	VacuumCostPageDirty = shared_params_data.cost_page_dirty;
+	VacuumCostPageHit = shared_params_data.cost_page_hit;
+	VacuumCostPageMiss = shared_params_data.cost_page_miss;
+
+	SpinLockRelease(&pv_shared_cost_params->mutex);
 
 	VacuumUpdateCosts();
 
@@ -640,9 +666,9 @@ parallel_vacuum_update_shared_delay_params(void)
  * some cost-based parameters to the supportive workers.
  */
 void
-parallel_vacuum_propagate_cost_based_params(void)
+parallel_vacuum_propagate_shared_delay_params(void)
 {
-	uint32	params_generation;
+	CostParamsData local_params_data;
 
 	Assert(AmAutoVacuumWorkerProcess());
 
@@ -650,24 +676,28 @@ parallel_vacuum_propagate_cost_based_params(void)
 	if (pv_shared_cost_params == NULL)
 		return;
 
-	params_generation =	pg_atomic_read_u32(&pv_shared_cost_params->generation);
+	FillCostParamsData(&local_params_data);
+	SpinLockAcquire(&pv_shared_cost_params->mutex);
 
-	SpinLockAcquire(&pv_shared_cost_params->spinlock);
+	if (CostParamsDataEqual(pv_shared_cost_params->params_data,
+							local_params_data))
+	{
+		/*
+		 * We don't need to update shared delay params if they haven't
+		 * changed.
+		 */
+		SpinLockRelease(&pv_shared_cost_params->mutex);
+		return;
+	}
 
-	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;
+	FillCostParamsData(&pv_shared_cost_params->params_data);
+	SpinLockRelease(&pv_shared_cost_params->mutex);
 
 	/*
 	 * Increase generation of the parameters, i.e. let parallel workers know
 	 * that they should re-read shared cost params.
 	 */
-	pg_atomic_write_u32(&pv_shared_cost_params->generation,
-						params_generation + 1);
-
-	SpinLockRelease(&pv_shared_cost_params->spinlock);
+	pg_atomic_fetch_add_u32(&pv_shared_cost_params->generation, 1);
 }
 
 /*
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index bd67e7748f0..b10829a9379 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -424,7 +424,7 @@ extern void parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs,
 												bool estimated_count,
 												PVWorkersUsage *wusage);
 extern void parallel_vacuum_update_shared_delay_params(void);
-extern void parallel_vacuum_propagate_cost_based_params(void);
+extern void parallel_vacuum_propagate_shared_delay_params(void);
 extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc);
 
 /* in commands/analyze.c */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 84bfa2970de..28b91d69086 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -544,6 +544,7 @@ CopyToRoutine
 CopyToState
 CopyToStateData
 Cost
+CostParamsData
 CostSelector
 Counters
 CoverExt
@@ -2066,6 +2067,7 @@ PVIndStats
 PVIndVacStatus
 PVOID
 PVShared
+PVSharedCostParams
 PVWorkersUsage
 PVWorkersStats
 PX_Alias
-- 
2.43.0

