From 6a35726700569e165204a4666b0c36d3b8d1bba2 Mon Sep 17 00:00:00 2001 From: Shinya Kato Date: Thu, 13 Nov 2025 13:06:33 +0900 Subject: [PATCH 1/2] Add mode and started_by columns to pg_stat_progress_vacuum. Exposes the vacuum mode (normal, aggressive, or failsafe) and the reason why the current VACUUM operation was initiated (manual, autovacuum, or autovacuum_wraparound). This allows users and monitoring tools to better understand VACUUM behavior. Bump catalog version. Author: Shinya Kato Reviewed-by: Kirill Reshke Reviewed-by: Nathan Bossart Reviewed-by: Robert Treat Reviewed-by: Masahiko Sawada Reviewed-by: Sami Imseih Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CAOzEurQcOY-OBL_ouEVfEaFqe_md3vB5pXjR_m6L71Dcp1JKCQ@mail.gmail.com --- doc/src/sgml/maintenance.sgml | 7 ++- doc/src/sgml/monitoring.sgml | 75 ++++++++++++++++++++++++++++ src/backend/access/heap/vacuumlazy.c | 23 +++++++-- src/backend/catalog/system_views.sql | 10 +++- src/include/commands/progress.h | 12 +++++ src/test/regress/expected/rules.out | 14 +++++- 6 files changed, 133 insertions(+), 8 deletions(-) diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml index f4f0433ef6f0..ab0cb02a3d23 100644 --- a/doc/src/sgml/maintenance.sgml +++ b/doc/src/sgml/maintenance.sgml @@ -1018,8 +1018,11 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu see . However, if the autovacuum is running to prevent transaction ID wraparound (i.e., the autovacuum query name in the pg_stat_activity view ends with - (to prevent wraparound)), the autovacuum is not - automatically interrupted. + (to prevent wraparound) or the + autovacuum_wraparound value in the + started_by column in the + pg_stat_progress_vacuum view), the autovacuum is + not automatically interrupted. diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index d2dd5e283658..cc2b590e7a23 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -6710,6 +6710,81 @@ FROM pg_stat_get_backend_idset() AS backendid; stale. + + + + mode text + + + The mode in which the current VACUUM operation is + running. See for details of each + mode. Possible values are: + + + + normal: The operation is performing a standard + vacuum. It is neither required to run in aggressive mode nor operating + in failsafe mode. + + + + + aggressive: The operation is running an aggressive + vacuum, which must scan every page that is not marked all-frozen. + The parameters and + determine when a + table requires aggressive vacuuming. + + + + + failsafe: The vacuum has entered failsafe mode, + in which it performs only the minimum work necessary to avoid + transaction ID or multixact ID wraparound failure. + The parameters and + determine when the + vacuum enters failsafe mode. The vacuum may start in this mode or + switch to it while running; the value of the + mode column may transition from another + mode to failsafe during the operation. + + + + + + + + + started_by text + + + Shows what caused the current VACUUM operation to be + started. Possible values are: + + + + manual: The vacuum was started by an explicit + VACUUM command. + + + + + autovacuum: The vacuum was started by an autovacuum + worker. Vacuums run by autovacuum workers may be interrupted due to + lock conflicts. + + + + + autovacuum_wraparound: The vacuum was started by an + autovacuum worker to prevent transaction ID or multixact ID + wraparound. Vacuums run for wraparound protection are not interrupted + due to lock conflicts. + + + + + diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 65bb0568a867..cc5b03eb7cea 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -664,6 +664,14 @@ heap_vacuum_rel(Relation rel, const VacuumParams params, pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, RelationGetRelid(rel)); + if (AmAutoVacuumWorkerProcess()) + pgstat_progress_update_param(PROGRESS_VACUUM_STARTED_BY, + params.is_wraparound + ? PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM_WRAPAROUND + : PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM); + else + pgstat_progress_update_param(PROGRESS_VACUUM_STARTED_BY, + PROGRESS_VACUUM_STARTED_BY_MANUAL); /* * Setup error traceback support for ereport() first. The idea is to set @@ -820,6 +828,12 @@ heap_vacuum_rel(Relation rel, const VacuumParams params, */ heap_vacuum_eager_scan_setup(vacrel, params); + /* Report the vacuum mode: 'normal' or 'aggressive' */ + pgstat_progress_update_param(PROGRESS_VACUUM_MODE, + vacrel->aggressive + ? PROGRESS_VACUUM_MODE_AGGRESSIVE + : PROGRESS_VACUUM_MODE_NORMAL); + if (verbose) { if (vacrel->aggressive) @@ -3001,9 +3015,10 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel) { const int progress_index[] = { PROGRESS_VACUUM_INDEXES_TOTAL, - PROGRESS_VACUUM_INDEXES_PROCESSED + PROGRESS_VACUUM_INDEXES_PROCESSED, + PROGRESS_VACUUM_MODE }; - int64 progress_val[2] = {0, 0}; + int64 progress_val[3] = {0, 0, PROGRESS_VACUUM_MODE_FAILSAFE}; VacuumFailsafeActive = true; @@ -3019,8 +3034,8 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel) vacrel->do_index_cleanup = false; vacrel->do_rel_truncate = false; - /* Reset the progress counters */ - pgstat_progress_update_multi_param(2, progress_index, progress_val); + /* Reset the progress counters and set the failsafe mode */ + pgstat_progress_update_multi_param(3, progress_index, progress_val); ereport(WARNING, (errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans", diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 48af8ee90a6d..e929c3547d5d 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1268,7 +1268,15 @@ CREATE VIEW pg_stat_progress_vacuum AS S.param6 AS max_dead_tuple_bytes, S.param7 AS dead_tuple_bytes, S.param8 AS num_dead_item_ids, S.param9 AS indexes_total, S.param10 AS indexes_processed, - S.param11 / 1000000::double precision AS delay_time + S.param11 / 1000000::double precision AS delay_time, + CASE S.param12 WHEN 1 THEN 'normal' + WHEN 2 THEN 'aggressive' + WHEN 3 THEN 'failsafe' + ELSE NULL END AS mode, + CASE S.param13 WHEN 1 THEN 'manual' + WHEN 2 THEN 'autovacuum' + WHEN 3 THEN 'autovacuum_wraparound' + ELSE NULL END AS started_by FROM pg_stat_get_progress_info('VACUUM') AS S LEFT JOIN pg_database D ON S.datid = D.oid; diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h index 1cde4bd9bcf1..45ed6e6e1cca 100644 --- a/src/include/commands/progress.h +++ b/src/include/commands/progress.h @@ -29,6 +29,8 @@ #define PROGRESS_VACUUM_INDEXES_TOTAL 8 #define PROGRESS_VACUUM_INDEXES_PROCESSED 9 #define PROGRESS_VACUUM_DELAY_TIME 10 +#define PROGRESS_VACUUM_MODE 11 +#define PROGRESS_VACUUM_STARTED_BY 12 /* Phases of vacuum (as advertised via PROGRESS_VACUUM_PHASE) */ #define PROGRESS_VACUUM_PHASE_SCAN_HEAP 1 @@ -38,6 +40,16 @@ #define PROGRESS_VACUUM_PHASE_TRUNCATE 5 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP 6 +/* Modes of vacuum (as advertised via PROGRESS_VACUUM_MODE) */ +#define PROGRESS_VACUUM_MODE_NORMAL 1 +#define PROGRESS_VACUUM_MODE_AGGRESSIVE 2 +#define PROGRESS_VACUUM_MODE_FAILSAFE 3 + +/* Reasons for vacuum (as advertised via PROGRESS_VACUUM_STARTED_BY) */ +#define PROGRESS_VACUUM_STARTED_BY_MANUAL 1 +#define PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM 2 +#define PROGRESS_VACUUM_STARTED_BY_AUTOVACUUM_WRAPAROUND 3 + /* Progress parameters for analyze */ #define PROGRESS_ANALYZE_PHASE 0 #define PROGRESS_ANALYZE_BLOCKS_TOTAL 1 diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 85d795dbd63b..1e5bb3cafcc9 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2105,7 +2105,19 @@ pg_stat_progress_vacuum| SELECT s.pid, s.param8 AS num_dead_item_ids, s.param9 AS indexes_total, s.param10 AS indexes_processed, - ((s.param11)::double precision / (1000000)::double precision) AS delay_time + ((s.param11)::double precision / (1000000)::double precision) AS delay_time, + CASE s.param12 + WHEN 1 THEN 'normal'::text + WHEN 2 THEN 'aggressive'::text + WHEN 3 THEN 'failsafe'::text + ELSE NULL::text + END AS mode, + CASE s.param13 + WHEN 1 THEN 'manual'::text + WHEN 2 THEN 'autovacuum'::text + WHEN 3 THEN 'autovacuum_wraparound'::text + ELSE NULL::text + END AS started_by 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_prefetch| SELECT stats_reset, From e6c0f39972a4505cf2cf21514e7b4504aad06382 Mon Sep 17 00:00:00 2001 From: Shinya Kato Date: Thu, 20 Nov 2025 14:56:05 +0900 Subject: [PATCH 2/2] Add started_by column to pg_stat_progress_analyze. Exposes the reason why the current ANALYZE operation was initiated (manual or autovacuum). This allows users and monitoring tools to better understand ANALYZE behavior. Bump catalog version. Author: Shinya Kato Discussion: https://postgr.es/m/CAA5RZ0suoicwxFeK_eDkUrzF7s0BVTaE7M%2BehCpYcCk5wiECpw%40mail.gmail.com --- doc/src/sgml/monitoring.sgml | 25 +++++++++++++++++++++++++ src/backend/catalog/system_views.sql | 5 ++++- src/backend/commands/analyze.c | 6 ++++++ src/include/commands/progress.h | 5 +++++ src/test/regress/expected/rules.out | 7 ++++++- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index cc2b590e7a23..d487a3a5c4e7 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -5770,6 +5770,31 @@ FROM pg_stat_get_backend_idset() AS backendid; zero). + + + + started_by text + + + Shows what caused the current ANALYZE operation to be + started. Possible values are: + + + + manual: The analyze was started by an explicit + ANALYZE or VACUUM with the + option. + + + + + autovacuum: The analyze was started by an + autovacuum worker. + + + + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index e929c3547d5d..0a0f95f6bb9f 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1247,7 +1247,10 @@ CREATE VIEW pg_stat_progress_analyze AS S.param6 AS child_tables_total, S.param7 AS child_tables_done, CAST(S.param8 AS oid) AS current_child_table_relid, - S.param9 / 1000000::double precision AS delay_time + S.param9 / 1000000::double precision AS delay_time, + CASE S.param10 WHEN 1 THEN 'manual' + WHEN 2 THEN 'autovacuum' + ELSE NULL END AS started_by FROM pg_stat_get_progress_info('ANALYZE') AS S LEFT JOIN pg_database D ON S.datid = D.oid; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 25089fae3e00..b2429170679a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -239,6 +239,12 @@ analyze_rel(Oid relid, RangeVar *relation, */ pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE, RelationGetRelid(onerel)); + if (AmAutoVacuumWorkerProcess()) + pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY, + PROGRESS_ANALYZE_STARTED_BY_AUTOVACUUM); + else + pgstat_progress_update_param(PROGRESS_ANALYZE_STARTED_BY, + PROGRESS_ANALYZE_STARTED_BY_MANUAL); /* * Do the normal non-recursive ANALYZE. We can skip this for partitioned diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h index 45ed6e6e1cca..9dc63a5a5bd5 100644 --- a/src/include/commands/progress.h +++ b/src/include/commands/progress.h @@ -60,6 +60,7 @@ #define PROGRESS_ANALYZE_CHILD_TABLES_DONE 6 #define PROGRESS_ANALYZE_CURRENT_CHILD_TABLE_RELID 7 #define PROGRESS_ANALYZE_DELAY_TIME 8 +#define PROGRESS_ANALYZE_STARTED_BY 9 /* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */ #define PROGRESS_ANALYZE_PHASE_ACQUIRE_SAMPLE_ROWS 1 @@ -68,6 +69,10 @@ #define PROGRESS_ANALYZE_PHASE_COMPUTE_EXT_STATS 4 #define PROGRESS_ANALYZE_PHASE_FINALIZE_ANALYZE 5 +/* Reasons for analyze (as advertised via PROGRESS_ANALYZE_STARTED_BY) */ +#define PROGRESS_ANALYZE_STARTED_BY_MANUAL 1 +#define PROGRESS_ANALYZE_STARTED_BY_AUTOVACUUM 2 + /* Progress parameters for cluster */ #define PROGRESS_CLUSTER_COMMAND 0 #define PROGRESS_CLUSTER_PHASE 1 diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 1e5bb3cafcc9..4286c266e17f 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1969,7 +1969,12 @@ pg_stat_progress_analyze| SELECT s.pid, s.param6 AS child_tables_total, s.param7 AS child_tables_done, (s.param8)::oid AS current_child_table_relid, - ((s.param9)::double precision / (1000000)::double precision) AS delay_time + ((s.param9)::double precision / (1000000)::double precision) AS delay_time, + CASE s.param10 + WHEN 1 THEN 'manual'::text + WHEN 2 THEN 'autovacuum'::text + ELSE NULL::text + END AS started_by FROM (pg_stat_get_progress_info('ANALYZE'::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_progress_basebackup| SELECT pid,