From a6583869eb884c02fc48f51074668379296fd77b Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Thu, 1 Oct 2026 10:39:53 +0900 Subject: [PATCH v8] Report per-index vacuum progress in pg_stat_progress_vacuum Blah. Bump catalog version. Author: Bharath Rupireddy Reviewed-by: Sami Imseih Reviewed-by: Michael Paquier Reviewed-by: Masahiko Sawada Discussion: https://postgr.es/m/CALj2ACUgwSchK6jQ2CdKLBWUADTOE_zKdTff2Zg3E6hOuXKv-w@mail.gmail.com --- src/include/commands/progress.h | 2 + src/backend/access/heap/vacuumlazy.c | 28 ++++++++++++- src/backend/catalog/system_views.sql | 5 ++- src/backend/commands/vacuumparallel.c | 31 +++++++++++++-- src/test/regress/expected/rules.out | 5 ++- doc/src/sgml/monitoring.sgml | 57 ++++++++++++++++++++++++++- 6 files changed, 119 insertions(+), 9 deletions(-) diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h index 2a12920c75f2..bf91455eff94 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/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 997d84a77b35..1f684b81f827 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 ad340887f541..809b9c0f1e48 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 d4572861000f..301cad428925 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -1080,6 +1080,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 @@ -1092,7 +1103,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; @@ -1102,6 +1113,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: @@ -1117,6 +1135,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 @@ -1195,8 +1215,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) @@ -1320,6 +1340,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); @@ -1339,6 +1362,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/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 4a8cc759d7b2..0addd043e68d 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, diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 0d038de1a238..b6d6f8e801c3 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 -- 2.55.0