Add mode and started_by columns to pg_stat_progress_vacuum view.
authorMasahiko Sawada <msawada@postgresql.org>
Tue, 9 Dec 2025 18:51:14 +0000 (10:51 -0800)
committerMasahiko Sawada <msawada@postgresql.org>
Tue, 9 Dec 2025 18:51:14 +0000 (10:51 -0800)
The new columns, mode and started_by, indicate the vacuum
mode ('normal', 'aggressive', or 'failsafe') and the initiator of the
vacuum ('manual', 'autovacuum', or 'autovacuum_wraparound'),
respectively. This allows users and monitoring tools to better
understand VACUUM behavior.

Bump catalog version.

Author: Shinya Kato <shinya11.kato@gmail.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: Robert Treat <rob@xzilla.net>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Yu Wang <wangyu_runtime@163.com>
Discussion: https://postgr.es/m/CAOzEurQcOY-OBL_ouEVfEaFqe_md3vB5pXjR_m6L71Dcp1JKCQ@mail.gmail.com

doc/src/sgml/maintenance.sgml
doc/src/sgml/monitoring.sgml
src/backend/access/heap/vacuumlazy.c
src/backend/catalog/system_views.sql
src/include/catalog/catversion.h
src/include/commands/progress.h
src/test/regress/expected/rules.out

index f4f0433ef6f0aaa79fa3cb17486dc37965410a01..08e6489afb8ed92b91c556012730afe0d3257f81 100644 (file)
@@ -1018,8 +1018,11 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
     see <xref linkend="table-lock-compatibility"/>.  However, if the autovacuum
     is running to prevent transaction ID wraparound (i.e., the autovacuum query
     name in the <structname>pg_stat_activity</structname> view ends with
-    <literal>(to prevent wraparound)</literal>), the autovacuum is not
-    automatically interrupted.
+    <literal>(to prevent wraparound)</literal> or the
+    <structfield>started_by</structfield> column in the
+    <structname>pg_stat_progress_vacuum</structname> view shows
+    <literal>autovacuum_wraparound</literal> value), the autovacuum is
+    not automatically interrupted.
    </para>
 
    <warning>
index d2dd5e2836587e4a5f2d1f59d6eab099ee73b892..cc2b590e7a23512569fa50a47bfcec9e76f917c5 100644 (file)
@@ -6710,6 +6710,81 @@ FROM pg_stat_get_backend_idset() AS backendid;
        stale.
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>mode</structfield> <type>text</type>
+      </para>
+      <para>
+       The mode in which the current <command>VACUUM</command> operation is
+       running. See <xref linkend="vacuum-for-wraparound"/> for details of each
+       mode. Possible values are:
+       <itemizedlist>
+        <listitem>
+         <para>
+          <literal>normal</literal>: The operation is performing a standard
+          vacuum. It is neither required to run in aggressive mode nor operating
+          in failsafe mode.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>aggressive</literal>: The operation is running an aggressive
+          vacuum, which must scan every page that is not marked all-frozen.
+          The parameters <xref linkend="guc-vacuum-freeze-table-age"/> and
+          <xref linkend="guc-vacuum-multixact-freeze-table-age"/> determine when a
+          table requires aggressive vacuuming.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>failsafe</literal>: 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 <xref linkend="guc-vacuum-failsafe-age"/> and
+          <xref linkend="guc-vacuum-multixact-failsafe-age"/> determine when the
+          vacuum enters failsafe mode. The vacuum may start in this mode or
+          switch to it while running; the value of the
+          <structfield>mode</structfield> column may transition from another
+          mode to <literal>failsafe</literal> during the operation.
+         </para>
+        </listitem>
+       </itemizedlist>
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>started_by</structfield> <type>text</type>
+      </para>
+      <para>
+       Shows what caused the current <command>VACUUM</command> operation to be
+       started. Possible values are:
+       <itemizedlist>
+        <listitem>
+         <para>
+          <literal>manual</literal>: The vacuum was started by an explicit
+          <command>VACUUM</command> command.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>autovacuum</literal>: The vacuum was started by an autovacuum
+          worker. Vacuums run by autovacuum workers may be interrupted due to
+          lock conflicts.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>autovacuum_wraparound</literal>: 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.
+         </para>
+        </listitem>
+       </itemizedlist>
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
index 65bb0568a867c7f498c43b2132e7fd82d0aca70e..cc5b03eb7ceac09c4634b23f592ce3bdc08c61a4 100644 (file)
@@ -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",
index 48af8ee90a6d5ad569a9595000b6c4781ddce185..e929c3547d5d9597fef991cea7daf03b88d14a2f 100644 (file)
@@ -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;
 
index 82dc84e4099dc147861265ca4890edc0a6e10688..3bd0f8499eb4e8c6d84cdacb9e55e6b8bcb308cc 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202512091
+#define CATALOG_VERSION_NO 202512092
 
 #endif
index 1cde4bd9bcf14a50c5ce7e1309841a9d2e5ad28a..45ed6e6e1ccadcbeb8fbab1b53256c9ea82396b2 100644 (file)
@@ -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
 #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
index 85d795dbd63b958ec3c72b1880757c1a8ec69d8e..1e5bb3cafcc9d17f004b5ac25357b9c3978c1ae5 100644 (file)
@@ -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,