From 302d369c81d269725a325fa72e820b42156aef31 Mon Sep 17 00:00:00 2001
From: TatsuyaKawata <kawatatatsuya0913@gmail.com>
Date: Sat, 22 Nov 2025 01:19:33 +0900
Subject: [PATCH v10] Add dead items memory usage to VACUUM (VERBOSE) and
 autovacuum logs.

This commit adds the total memory allocated during vacuum, the number
of times the dead items storage was reset, and the configured memory
limit. This helps users understand how much memory VACUUM required,
and such information can be used to avoid multiple index scans.

Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CAHza6qcPitBCkyiKJosDTt3bmxMvzZOTONoebwCkBZrr3rk65Q%40mail.gmail.com
---
 src/backend/access/heap/vacuumlazy.c | 33 ++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 30778a15639..3ff1f1fddfd 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -347,6 +347,8 @@ typedef struct LVRelState
 
 	/* Instrumentation counters */
 	int			num_index_scans;
+	int			num_dead_items_resets;
+	Size		total_dead_items_bytes;
 	/* Counters that follow are only for scanned_pages */
 	int64		tuples_deleted; /* # deleted from table */
 	int64		tuples_frozen;	/* # newly frozen */
@@ -645,6 +647,7 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 	BufferUsage startbufferusage = pgBufferUsage;
 	ErrorContextCallback errcallback;
 	char	  **indnames = NULL;
+	Size		dead_items_max_bytes = 0;
 
 	verbose = (params.options & VACOPT_VERBOSE) != 0;
 	instrument = (verbose || (AmAutoVacuumWorkerProcess() &&
@@ -767,6 +770,8 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 
 	/* Initialize remaining counters (be tidy) */
 	vacrel->num_index_scans = 0;
+	vacrel->num_dead_items_resets = 0;
+	vacrel->total_dead_items_bytes = 0;
 	vacrel->tuples_deleted = 0;
 	vacrel->tuples_frozen = 0;
 	vacrel->lpdead_items = 0;
@@ -864,6 +869,14 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 	 */
 	lazy_scan_heap(vacrel);
 
+	/*
+	 * Save dead items max_bytes and update the memory usage statistics before
+	 * cleanup, they are freed in parallel vacuum cases during
+	 * dead_items_cleanup().
+	 */
+	dead_items_max_bytes = vacrel->dead_items_info->max_bytes;
+	vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
+
 	/*
 	 * Free resources managed by dead_items_alloc.  This ends parallel mode in
 	 * passing when necessary.
@@ -1167,6 +1180,22 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
 							 walusage.wal_bytes,
 							 walusage.wal_fpi_bytes,
 							 walusage.wal_buffers_full);
+
+			/*
+			 * Report the dead items memory usage.
+			 *
+			 * The num_dead_items_resets counter increases when we reset the
+			 * collected dead items, so the counter is non-zero if at least
+			 * one dead items are collected, even if index vacuuming is
+			 * disabled.
+			 */
+			appendStringInfo(&buf,
+							 ngettext("memory usage: %.2f MB in total, with dead item storage reset %d time (limit was %.2f MB)\n",
+									  "memory usage: %.2f MB in total, with dead item storage reset %d times (limit was %.2f MB)\n",
+									  vacrel->num_dead_items_resets),
+							 (double) vacrel->total_dead_items_bytes / (1024 * 1024),
+							 vacrel->num_dead_items_resets,
+							 (double) dead_items_max_bytes / (1024 * 1024));
 			appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));
 
 			ereport(verbose ? INFO : LOG,
@@ -3617,6 +3646,10 @@ dead_items_add(LVRelState *vacrel, BlockNumber blkno, OffsetNumber *offsets,
 static void
 dead_items_reset(LVRelState *vacrel)
 {
+	/* Update statistics for dead items */
+	vacrel->num_dead_items_resets++;
+	vacrel->total_dead_items_bytes += TidStoreMemoryUsage(vacrel->dead_items);
+
 	if (ParallelVacuumIsActive(vacrel))
 	{
 		parallel_vacuum_reset_dead_items(vacrel->pvs);
-- 
2.34.1

