From 1378aa13430e264a990a18597e9d8fdae740927e Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Fri, 2 Oct 2026 07:29:22 +0900 Subject: [PATCH] Report per-index vacuum progress in pg_stat_progress_vacuum pg_stat_progress_vacuum gains three fields, reflecting some activity related to index cleanups during VACUUM, which is a phase that could take a long time depending on the quantity of data to treat and the number of indexes: - current_index_relid, the index being processed. - index_blks_total, total number of blocks to process. - index_blks_done, number of blocks already processed. There fields are new, and do not reuse the existing fields related to heap as index cleanups may need multiple passes, where knowing both the heap activity and the index activity could be relevant. This is unlikely since tids are accumulated in the heap scanning phase using an optimized radix tree, still possible. VACUUM can spawn parallel workers to clean indexes (each worker cleans a different index); these should up in the progress view in the shape of one extra row for each worker. Bump catalog version. Author: Bharath Rupireddy Reviewed-by: Sami Imseih Reviewed-by: Michael Paquier Reviewed-by: Masahiko Sawada Reviewed-by: Shihao Zhong Discussion: https://postgr.es/m/CALj2ACUgwSchK6jQ2CdKLBWUADTOE_zKdTff2Zg3E6hOuXKv-w@mail.gmail.com --- doc/src/sgml/monitoring.sgml | 57 ++++++++++++++++++++++++++- src/backend/access/heap/vacuumlazy.c | 28 ++++++++++++- src/backend/catalog/system_views.sql | 5 ++- src/backend/commands/vacuumparallel.c | 31 +++++++++++++-- src/include/catalog/catversion.h | 2 +- src/include/commands/progress.h | 2 + src/test/regress/expected/rules.out | 5 ++- 7 files changed, 120 insertions(+), 10 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 0d038de1a23..6337d2a3d25 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -7685,8 +7685,10 @@ FROM pg_stat_get_backend_idset() AS backendid; Whenever VACUUM is running, the pg_stat_progress_vacuum view will contain - one row for each backend (including autovacuum worker processes) that is - currently vacuuming. The tables below describe the information + one row for each backend (including autovacuum worker processes and + parallel workers launched for + parallel vacuum) that is currently + vacuuming. The tables below describe the information that will be reported and provide information about how to interpret it. Progress for VACUUM FULL commands is reported via pg_stat_progress_repack, and is also visible via @@ -7944,10 +7946,61 @@ FROM pg_stat_get_backend_idset() AS backendid; + + + + current_index_relid oid + + + OID of the index currently being vacuumed, or 0 if none is. This + field is only valid when the phase is + vacuuming indexes or + cleaning up indexes. + + + + + + index_blks_total bigint + + + Total number of blocks in the index identified by + current_index_relid. Only B-tree indexes + report this; it is 0 for other index access methods, and when no index + scan is in progress. + + + + + + index_blks_done bigint + + + Number of blocks of that index scanned so far. It stops one block + short of index_blks_total, which counts the + index metapage, never scanned. + + + + + + During a parallel vacuum, the leader and each of its workers reports its + own row, all sharing the same relid. + Only phase, + current_index_relid, + index_blks_total and + index_blks_done are maintained on a worker row; + the remaining columns track command-level progress that only the leader + maintains. Use leader_pid in + pg_stat_activity + to tell the leader row from its worker rows. + + + VACUUM Phases diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 997d84a77b3..1f684b81f82 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -3038,17 +3038,27 @@ 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.is_autovacuum = AmAutoVacuumWorkerProcess(); - ivinfo.report_progress = false; + ivinfo.report_progress = true; ivinfo.estimated_count = true; ivinfo.message_level = DEBUG2; ivinfo.num_heap_tuples = reltuples; ivinfo.strategy = vacrel->bstrategy; + /* Report which index we're currently processing */ + pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, + (int64) RelationGetRelid(indrel)); + /* * Update error traceback information. * @@ -3070,6 +3080,8 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat, pfree(vacrel->indname); vacrel->indname = NULL; + pgstat_progress_update_multi_param(3, reset_index, reset_val); + return istat; } @@ -3089,18 +3101,28 @@ 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.is_autovacuum = AmAutoVacuumWorkerProcess(); - ivinfo.report_progress = false; + ivinfo.report_progress = true; ivinfo.estimated_count = estimated_count; ivinfo.message_level = DEBUG2; ivinfo.num_heap_tuples = reltuples; ivinfo.strategy = vacrel->bstrategy; + /* Report which index we're currently processing */ + pgstat_progress_update_param(PROGRESS_VACUUM_CURRENT_INDEX_RELID, + (int64) RelationGetRelid(indrel)); + /* * Update error traceback information. * @@ -3120,6 +3142,8 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat, pfree(vacrel->indname); vacrel->indname = NULL; + 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 ad340887f54..809b9c0f1e4 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1353,7 +1353,10 @@ CREATE VIEW pg_stat_progress_vacuum AS CASE S.param13 WHEN 1 THEN 'manual' WHEN 2 THEN 'autovacuum' WHEN 3 THEN 'autovacuum_wraparound' - ELSE NULL END AS started_by + 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 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 da0ec3cc8ee..ac8ad6637b5 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -1115,6 +1115,17 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, IndexBulkDeleteResult *istat = NULL; IndexBulkDeleteResult *istat_res; IndexVacuumInfo ivinfo; + const int progress_index[] = { + PROGRESS_VACUUM_PHASE, + 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 @@ -1127,7 +1138,7 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, ivinfo.heaprel = pvs->heaprel; ivinfo.analyze_only = false; ivinfo.is_autovacuum = pvs->shared->is_autovacuum; - 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; @@ -1137,6 +1148,13 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, pvs->indname = pstrdup(RelationGetRelationName(indrel)); pvs->status = indstats->status; + /* Report the phase and the index we're about to process */ + progress_val[0] = (indstats->status == PARALLEL_INDVAC_STATUS_NEED_BULKDELETE) + ? PROGRESS_VACUUM_PHASE_VACUUM_INDEX + : PROGRESS_VACUUM_PHASE_INDEX_CLEANUP; + progress_val[1] = (int64) RelationGetRelid(indrel); + pgstat_progress_update_multi_param(2, progress_index, progress_val); + switch (indstats->status) { case PARALLEL_INDVAC_STATUS_NEED_BULKDELETE: @@ -1152,6 +1170,8 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel, RelationGetRelationName(indrel)); } + pgstat_progress_update_multi_param(3, reset_index, reset_val); + /* * Copy the index bulk-deletion result returned from ambulkdelete and * amvacuumcleanup to the DSM segment if it's the first cycle because they @@ -1230,8 +1250,8 @@ parallel_vacuum_index_is_parallel_safe(Relation indrel, int num_index_scans, /* * Perform work within a launched parallel process. * - * Since parallel vacuum workers perform only index vacuum or index cleanup, - * we don't need to report progress information. + * Parallel vacuum workers perform only index vacuum or index cleanup; they + * report progress for the index they are processing. */ void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc) @@ -1355,6 +1375,9 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc) /* Prepare to track buffer usage during parallel execution */ InstrStartParallelQuery(); + /* Register this worker for vacuum progress reporting */ + pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, shared->relid); + /* Process indexes to perform vacuum/cleanup */ parallel_vacuum_process_safe_indexes(&pvs); @@ -1374,6 +1397,8 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc) /* Pop the error context stack */ error_context_stack = errcallback.previous; + pgstat_progress_end_command(); + vac_close_indexes(nindexes, indrels, RowExclusiveLock); table_close(rel, ShareUpdateExclusiveLock); FreeAccessStrategy(pvs.bstrategy); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 6f3e526de96..7f7667f6179 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202609152 +#define CATALOG_VERSION_NO 202610021 #endif diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h index 2a12920c75f..bf91455eff9 100644 --- a/src/include/commands/progress.h +++ b/src/include/commands/progress.h @@ -31,6 +31,8 @@ #define PROGRESS_VACUUM_DELAY_TIME 10 #define PROGRESS_VACUUM_MODE 11 #define PROGRESS_VACUUM_STARTED_BY 12 +#define PROGRESS_VACUUM_CURRENT_INDEX_RELID 13 +/* 15 and 16 reserved for "block number" metrics */ /* Phases of vacuum (as advertised via PROGRESS_VACUUM_PHASE) */ #define PROGRESS_VACUUM_PHASE_SCAN_HEAP 1 diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 4a8cc759d7b..0addd043e68 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2214,7 +2214,10 @@ pg_stat_progress_vacuum| SELECT s.pid, WHEN 2 THEN 'autovacuum'::text WHEN 3 THEN 'autovacuum_wraparound'::text ELSE NULL::text - END AS started_by + END AS started_by, + (s.param14)::oid AS current_index_relid, + s.param16 AS index_blks_total, + s.param17 AS index_blks_done 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))); pg_stat_recovery| SELECT promote_triggered,