From 38872ca5a572f9995cdf7ff891efdbb5f3a821ae Mon Sep 17 00:00:00 2001
From: TatsuyaKawata <kawatatatsuya0913@gmail.com>
Date: Thu, 20 Nov 2025 02:12:22 +0900
Subject: [PATCH v3] Add memory usage reporting to VACUUM VERBOSE

---
 src/backend/access/heap/vacuumlazy.c | 34 ++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index deb9a3dc0d1..b0395a5d2e4 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -355,6 +355,10 @@ typedef struct LVRelState
 	int64		recently_dead_tuples;	/* # dead, but not yet removable */
 	int64		missed_dead_tuples; /* # removable, but not removed */
 
+	/* Total memory usage tracking for dead_items */
+	Size		total_dead_items_bytes;
+	Size		dead_items_max_bytes;	/* save max_bytes before cleanup */
+
 	/* State maintained by heap_vac_scan_next_block() */
 	BlockNumber current_block;	/* last block returned */
 	BlockNumber next_unskippable_block; /* next unskippable block */
@@ -765,6 +769,7 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 	vacrel->live_tuples = 0;
 	vacrel->recently_dead_tuples = 0;
 	vacrel->missed_dead_tuples = 0;
+	vacrel->total_dead_items_bytes = 0;
 
 	vacrel->vm_new_visible_pages = 0;
 	vacrel->vm_new_visible_frozen_pages = 0;
@@ -844,6 +849,12 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 	lazy_check_wraparound_failsafe(vacrel);
 	dead_items_alloc(vacrel, params.nworkers);
 
+	/*
+	 * Save max_bytes before cleanup, as dead_items_info may be freed in
+	 * parallel mode during dead_items_cleanup().
+	 */
+	vacrel->dead_items_max_bytes = vacrel->dead_items_info->max_bytes;
+
 	/*
 	 * Call lazy_scan_heap to perform all required heap pruning, index
 	 * vacuuming, and heap vacuuming (plus related processing)
@@ -1154,6 +1165,17 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 							 walusage.wal_bytes,
 							 walusage.wal_fpi_bytes,
 							 walusage.wal_buffers_full);
+			/*
+			 * Report memory usage for dead_items tracking (skip
+			 * if no indexes)
+			 */
+			if (vacrel->nindexes > 0) {
+				appendStringInfo(&buf,
+								_("memory usage: total %.2f MB used across %d index scan(s) (max %.2f MB at once)\n"),
+								(double) vacrel->total_dead_items_bytes / (1024.0 * 1024.0),
+								vacrel->nindexes,
+								(double) vacrel->dead_items_max_bytes / (1024.0 * 1024.0));
+			}
 			appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));
 
 			ereport(verbose ? INFO : LOG,
@@ -2478,6 +2500,9 @@ lazy_vacuum(LVRelState *vacrel)
 	if (!vacrel->do_index_vacuuming)
 	{
 		Assert(!vacrel->do_index_cleanup);
+		if (vacrel->dead_items != NULL) {
+			vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
+		}
 		dead_items_reset(vacrel);
 		return;
 	}
@@ -2551,6 +2576,9 @@ lazy_vacuum(LVRelState *vacrel)
 		 * calls.)
 		 */
 		vacrel->do_index_vacuuming = false;
+		if (vacrel->dead_items != NULL) {
+			vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
+		}
 	}
 	else if (lazy_vacuum_all_indexes(vacrel))
 	{
@@ -2559,6 +2587,9 @@ lazy_vacuum(LVRelState *vacrel)
 		 * heap vacuuming now.
 		 */
 		lazy_vacuum_heap_rel(vacrel);
+		if (vacrel->dead_items != NULL) {
+			vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
+		}
 	}
 	else
 	{
@@ -2574,6 +2605,9 @@ lazy_vacuum(LVRelState *vacrel)
 		 * back here again.
 		 */
 		Assert(VacuumFailsafeActive);
+		if (vacrel->dead_items != NULL) {
+			vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
+		}
 	}
 
 	/*
-- 
2.34.1

