From 8b6fd4254165d8551a15f84b0e2973e134ce3938 Mon Sep 17 00:00:00 2001
From: Daniil Davidov <d.davydov@postgrespro.ru>
Date: Mon, 16 Mar 2026 19:09:01 +0700
Subject: [PATCH v26 6/6] Advanced logging for parallel autovacuum

---
 src/backend/access/heap/vacuumlazy.c  | 40 +++++++++++++++++++++------
 src/backend/commands/vacuumparallel.c |  6 ++++
 src/include/commands/vacuum.h         | 15 ++++++++--
 3 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 4f97baced2b..df709afe622 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -791,8 +791,10 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 
 	vacrel->workers_usage.vacuum.nlaunched = 0;
 	vacrel->workers_usage.vacuum.nplanned = 0;
+	vacrel->workers_usage.vacuum.nreserved = 0;
 	vacrel->workers_usage.cleanup.nlaunched = 0;
 	vacrel->workers_usage.cleanup.nplanned = 0;
+	vacrel->workers_usage.cleanup.nreserved = 0;
 
 	/*
 	 * Get cutoffs that determine which deleted tuples are considered DEAD,
@@ -1146,17 +1148,39 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 							 vacrel->lpdead_items);
 			if (vacrel->workers_usage.vacuum.nplanned > 0)
 			{
-				appendStringInfo(&buf,
-								 _("parallel workers: index vacuum: %d planned, %d launched in total\n"),
-								 vacrel->workers_usage.vacuum.nplanned,
-								 vacrel->workers_usage.vacuum.nlaunched);
+				if (AmAutoVacuumWorkerProcess())
+				{
+					appendStringInfo(&buf,
+									 _("parallel workers: index vacuum: %d planned, %d reserved, %d launched in total\n"),
+									 vacrel->workers_usage.vacuum.nplanned,
+									 vacrel->workers_usage.vacuum.nreserved,
+									 vacrel->workers_usage.vacuum.nlaunched);
+				}
+				else
+				{
+					appendStringInfo(&buf,
+									 _("parallel workers: index vacuum: %d planned, %d launched in total\n"),
+									 vacrel->workers_usage.vacuum.nplanned,
+									 vacrel->workers_usage.vacuum.nlaunched);
+				}
 			}
 			if (vacrel->workers_usage.cleanup.nplanned > 0)
 			{
-				appendStringInfo(&buf,
-								 _("parallel workers: index cleanup: %d planned, %d launched\n"),
-								 vacrel->workers_usage.cleanup.nplanned,
-								 vacrel->workers_usage.cleanup.nlaunched);
+				if (AmAutoVacuumWorkerProcess())
+				{
+					appendStringInfo(&buf,
+									 _("parallel workers: index cleanup: %d planned, %d reserved, %d launched\n"),
+									 vacrel->workers_usage.cleanup.nplanned,
+									 vacrel->workers_usage.cleanup.nreserved,
+									 vacrel->workers_usage.cleanup.nlaunched);
+				}
+				else
+				{
+					appendStringInfo(&buf,
+									 _("parallel workers: index cleanup: %d planned, %d launched\n"),
+									 vacrel->workers_usage.cleanup.nplanned,
+									 vacrel->workers_usage.cleanup.nlaunched);
+				}
 			}
 			for (int i = 0; i < vacrel->nindexes; i++)
 			{
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index 650060871d3..5105137ce3b 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -837,8 +837,14 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan
 	 * fewer workers than we requested.
 	 */
 	if (AmAutoVacuumWorkerProcess() && nworkers > 0)
+	{
 		AutoVacuumReserveParallelWorkers(&nworkers);
 
+		/* Remember this value, if we asked to */
+		if (wstats != NULL)
+			wstats->nreserved += nworkers;
+	}
+
 	/*
 	 * Set index vacuum status and mark whether parallel vacuum worker can
 	 * process it.
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index cf0c3c9dbf7..4bfeba8264d 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -308,13 +308,24 @@ typedef struct PVWorkersStats
 	/* Number of parallel workers we are planned to launch */
 	int			nplanned;
 
+	/*
+	 * Number of parallel workers we have managed to reserve.
+	 *
+	 * Note, that we collect this stats only for the parallel *autovacuum*
+	 * since during it we must reserve workers in shared state before actually
+	 * trying to launch them (in order to meet the
+	 * autovacuum_max_parallel_workers limit). Manual VACUUM (PARALLEL), on
+	 * the contrary, doesn't need to reserve workers.
+	 */
+	int			nreserved;
+
 	/* Number of launched parallel workers */
 	int			nlaunched;
 } PVWorkersStats;
 
 /*
- * PVWorkersUsage stores information about total number of launched and
- * planned workers during parallel vacuum (both for vacuum and cleanup).
+ * PVWorkersUsage stores information about total number of launched, reserved
+ * and planned workers during parallel vacuum (both for vacuum and cleanup).
  */
 typedef struct PVWorkersUsage
 {
-- 
2.43.0

