From 4cd92806c6e6a38a9913b9c37c85e498071c1dca Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Tue, 25 Aug 2026 03:20:56 +0000 Subject: [PATCH v4 2/2] Report index block progress in pg_stat_progress_vacuum. Previously, pg_stat_progress_vacuum reported which index a backend was vacuuming but not how far it had gotten through that index. This matters most for B-tree, the common index type: for a large B-tree, at the scale of hundreds of GBs to TBs, per-index progress is what lets one estimate when the index phase, and together with heap_blks_* the whole vacuum, will finish. This commit reuses the block counters that CREATE INDEX progress reporting added in commit ab0dfc961 (PROGRESS_SCAN_BLOCKS_TOTAL and PROGRESS_SCAN_BLOCKS_DONE), exposing them as new index_blks_total and index_blks_done columns. B-tree's bulk-delete scan already knows how to report them, so this commit only turns that reporting on in the serial and parallel index-vacuum paths. Like current_index_relid, the counters are reported per participating backend, so each worker's row shows progress for the index it is scanning. These counters are kept separate from heap_blks_*. The heap block counters must be retained across a multi-pass index vacuum, which happens when the dead-TID store fills, and in the serial case the leader does the index vacuuming itself, so reusing the heap counters for index blocks would corrupt heap progress that is still needed. No caller sets IndexVacuumInfo.report_progress to false anymore; removing it is parked for a follow-up commit. Author: Bharath Rupireddy Reviewed-by: Michael Paquier Reviewed-by: Sami Imseih Discussion: https://postgr.es/m/CALj2ACX6gyBmQfaqbCsycDmaSPbq1=iPJw1OqUT+aLqaKjW8dQ@mail.gmail.com --- doc/src/sgml/monitoring.sgml | 27 +++++++++++++++++++++- src/backend/access/heap/vacuumlazy.c | 32 ++++++++++++++++++++------- src/backend/catalog/system_views.sql | 2 ++ src/backend/commands/vacuumparallel.c | 16 ++++++++++---- src/test/regress/expected/rules.out | 2 ++ 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 0d3446d6e86..0f5dc69a246 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -7929,6 +7929,29 @@ FROM pg_stat_get_backend_idset() AS backendid; + + + index_blks_total bigint + + + Total number of blocks to scan for the B-tree index identified by + current_index_relid. It is reset to 0 once + the index has been processed. + + + + + + index_blks_done bigint + + + Number of blocks scanned so far for the B-tree index identified by + current_index_relid. Together with + index_blks_total this gives per-index vacuum + progress, which is useful for estimating completion of large indexes. + + + leader_pid integer @@ -7952,7 +7975,9 @@ FROM pg_stat_get_backend_idset() AS backendid; During a parallel vacuum, each participating backend (the leader and each parallel worker) reports its own row. Only a subset of the columns is meaningful on a worker row: phase, - current_index_relid and + current_index_relid, + index_blks_total, + index_blks_done and leader_pid. The remaining columns track command-level heap progress and are maintained only on the leader row; they read as zero on worker rows. leader_pid is diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 8bc3de84581..459fff8df1d 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -3038,11 +3038,17 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + const int reset_index[] = { + PROGRESS_VACUUM_CURRENT_INDEX_RELID, + PROGRESS_SCAN_BLOCKS_TOTAL, + PROGRESS_SCAN_BLOCKS_DONE + }; + const int64 reset_val[] = {(int64) InvalidOid, 0, 0}; ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; ivinfo.analyze_only = false; - ivinfo.report_progress = false; + ivinfo.report_progress = true; ivinfo.estimated_count = true; ivinfo.message_level = DEBUG2; ivinfo.num_heap_tuples = reltuples; @@ -3073,9 +3079,11 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, pfree(vacrel->indname); vacrel->indname = NULL; - /* Reset the current index relid to avoid reporting a stale value */ - pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, - (int64) InvalidOid); + /* + * Reset the current index progress parameters to avoid reporting stale + * values. + */ + pgstat_progress_update_multi_param(3, reset_index, reset_val); return istat; } @@ -3096,11 +3104,17 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, { IndexVacuumInfo ivinfo; LVSavedErrInfo saved_err_info; + const int reset_index[] = { + PROGRESS_VACUUM_CURRENT_INDEX_RELID, + PROGRESS_SCAN_BLOCKS_TOTAL, + PROGRESS_SCAN_BLOCKS_DONE + }; + const int64 reset_val[] = {(int64) InvalidOid, 0, 0}; ivinfo.index = indrel; ivinfo.heaprel = vacrel->rel; ivinfo.analyze_only = false; - ivinfo.report_progress = false; + ivinfo.report_progress = true; ivinfo.estimated_count = estimated_count; ivinfo.message_level = DEBUG2; @@ -3130,9 +3144,11 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, pfree(vacrel->indname); vacrel->indname = NULL; - /* Reset the current index relid to avoid reporting a stale value */ - pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, - (int64) InvalidOid); + /* + * Reset the current index progress parameters to avoid reporting stale + * values. + */ + pgstat_progress_update_multi_param(3, reset_index, reset_val); return istat; } diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 64a627b37b2..741e8f4fa36 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1355,6 +1355,8 @@ CREATE VIEW pg_stat_progress_vacuum AS WHEN 3 THEN 'autovacuum_wraparound' ELSE NULL END AS started_by, CAST(S.param14 AS oid) AS current_index_relid, + S.param16 AS index_blks_total, + S.param17 AS index_blks_done, A.leader_pid AS leader_pid FROM pg_stat_get_progress_info('VACUUM') AS S LEFT JOIN pg_database D ON S.datid = D.oid diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index d6a56f63a0f..be87b1937ad 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -1081,6 +1081,12 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, PROGRESS_VACUUM_CURRENT_INDEX_RELID }; int64 progress_val[2]; + const int reset_index[] = { + PROGRESS_VACUUM_CURRENT_INDEX_RELID, + PROGRESS_SCAN_BLOCKS_TOTAL, + PROGRESS_SCAN_BLOCKS_DONE + }; + const int64 reset_val[] = {(int64) InvalidOid, 0, 0}; /* * Update the pointer to the corresponding bulk-deletion result if someone @@ -1092,7 +1098,7 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, ivinfo.index = indrel; ivinfo.heaprel = pvs->heaprel; ivinfo.analyze_only = false; - ivinfo.report_progress = false; + ivinfo.report_progress = true; ivinfo.message_level = DEBUG2; ivinfo.estimated_count = pvs->shared->estimated_count; ivinfo.num_heap_tuples = pvs->shared->reltuples; @@ -1127,9 +1133,11 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, RelationGetRelationName(indrel)); } - /* Reset the current index relid to avoid reporting a stale value */ - pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, - (int64) InvalidOid); + /* + * Reset the current index progress parameters to avoid reporting stale + * values. + */ + pgstat_progress_update_multi_param(3, reset_index, reset_val); /* * Copy the index bulk-deletion result returned from ambulkdelete and diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 17caf9418f2..c5ad0881611 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2215,6 +2215,8 @@ pg_stat_progress_vacuum| SELECT s.pid, ELSE NULL::text END AS started_by, (s.param14)::oid AS current_index_relid, + s.param16 AS index_blks_total, + s.param17 AS index_blks_done, a.leader_pid FROM ((pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15, param16, param17, param18, param19, param20) LEFT JOIN pg_database d ON ((s.datid = d.oid))) -- 2.47.3