public inbox for [email protected]  
help / color / mirror / Atom feed
Add pg_stat_autovacuum_priority
60+ messages / 8 participants
[nested] [flat]

* Add pg_stat_autovacuum_priority
@ 2026-03-27 23:14 Sami Imseih <[email protected]>
  2026-03-28 04:14 ` Re: Add pg_stat_autovacuum_priority SATYANARAYANA NARLAPURAM <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 15:32 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 3 replies; 60+ messages in thread

From: Sami Imseih @ 2026-03-27 23:14 UTC (permalink / raw)
  To: pgsql-hackers

Hi,

This is a quick follow-up to the commit d7965d65f which
introduced autovacuum prioritization based on a score that
is the Max of several components, such as vacuum
thresholds, xid age, etc.

It was also discussed in that thread [1] that we will need
a view to expose the priority scores, per table in a view.
This will allow a user to introspect what the autovacuum
launcher will prioritize next as well as verify tuning
efforts for autovacuum prioritization; the latter case
likely being rare.

So after spending time on this today, I am proposing a view
that returns a line for each relation with information
about if the table needs autovacuum/autoanalyze, as well as
scores of each component and the Max score. It looks like
the below:

```
postgres=# select * FROM pg_stat_autovacuum_priority;
-[ RECORD 1 ]-----+----------------------------
relid             | 16410
schemaname        | public
relname           | av_priority_test
needs_vacuum      | f
needs_analyze     | f
wraparound        | f
score             | 0
xid_score         | 0
mxid_score        | 0
vacuum_dead_score | 0
vacuum_ins_score  | 0
analyze_score     | 0
```

The function essentially calls relation_needs_vacanalyze()
with some setup work, such as scanning the catalog with an
AccessShareLock, etc. and emits the result of this call.

To make this work 0001 introduces a small change to
relation_needs_vacanalyze() to take in a boolean to force
the calculation of the score (even if autovacuum is
disabled for the relation).

0002 introduces the view with documentation and testing in
vacuum.c (xid age and mxid age scores are not tested as
they require xid_wraparound to consume enough XIDs to
trigger a score, which will cost too much time for a
regression test).

Find the attached taking the first attempt at this view.

[1] [https://www.postgresql.org/message-id/CAApHDvqQN-B2sQov8nsfZOmx-VeJMauSf4kLa3A8LsK1tUyBNw%40mail.gma...]

--
Sami Imseih
Amazon Web Services (AWS)


Attachments:

  [application/octet-stream] v1-0002-Add-pg_stat_autovacuum_priority-view.patch (19.5K, 2-v1-0002-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From a5bc5aaf9b468d08a879983481199d4ea82f49d3 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v1 2/2] Add pg_stat_autovacuum_priority view

d7965d65f introduced vacuum prioritization based on a score that
calculates the urgency based on a Max of several components, such as
vacuum thresholds, xid age, etc. These scores are used by autovacuum
to prioritize which tables to vacuum next.

The aforementioned commit includes documentation into how
prioritization works and several GUCs to tune priority.

This change introduces a view to show a line per relation with vacuum
or analyze eligibility, the score of each component and the Max score
across all components. Having this information exposed provides the
user with a way to introspect which tables are eligible for autovacuum
or autoanalyze as well as provide feedback on tuning of the GUCs.

The view simply calls relation_needs_vacanalyze() which provides the
scores and eligibility details. An earlier commit also introduced the
force_scores field to relation_needs_vacanalyze() so it can compute
scores regardless of the table's eligibility for autovacuum or
autoanalyze.

The view also emits the relid, namespace and relname, therefore it can
be joined with other views like pg_stat_all_tables and
pg_stat_progress_vacuum for complementary vacuum details.

Tests added to vacuum.sql
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 158 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  18 +++
 src/backend/postmaster/autovacuum.c  | 113 +++++++++++++++++++
 src/include/catalog/catversion.h     |   2 +-
 src/include/catalog/pg_proc.dat      |   9 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  30 +++++
 src/test/regress/sql/vacuum.sql      |  21 ++++
 9 files changed, 371 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..39508c6e979 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -2856,6 +2865,155 @@ description | Waiting for a newly initialized WAL file to reach durable storage
   </para>
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if autovacuum considers this table in need of vacuuming
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if autovacuum considers this table in need of analyzing
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>xid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age to prevent
+       transaction ID wraparound.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>mxid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age to prevent
+       multixact ID wraparound.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_dead_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_ins_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum, reflecting the need for an insert-triggered vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze, reflecting the need for updated statistics.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect2>
+
  <sect2 id="monitoring-pg-stat-io-view">
   <title><structname>pg_stat_io</structname></title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..41cda990135 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,24 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.xid_score,
+            S.mxid_score,
+            S.vacuum_dead_score,
+            S.vacuum_ins_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f6b213852e3..ccb4118df51 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -390,6 +392,10 @@ static void perform_work_item(AutoVacuumWorkItem *workitem);
 static void autovac_report_activity(autovac_table *tab);
 static void autovac_report_workitem(AutoVacuumWorkItem *workitem,
 									const char *nspname, const char *relname);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumScores *scores);
 static void avl_sigusr2_handler(SIGNAL_ARGS);
 static bool av_worker_available(void);
 static void check_av_worker_gucs(void);
@@ -3679,3 +3685,110 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * compute_autovac_score
+ *		Wrapper around relation_needs_vacanalyze() that handles the
+ *		per-relation setup similar to do_autovacuum() before calling
+ *		relation_needs_vacanalyze().
+ */
+static void
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumScores *scores)
+{
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+	AutoVacOpts *relopts;
+	PgStat_StatTabEntry *tabentry;
+
+	relopts = extract_autovac_opts(tuple, pg_class_desc);
+
+	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
+											  classForm->oid);
+
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
+							  effective_multixact_freeze_max_age, true,
+							  dovacuum, doanalyze, wraparound, scores);
+
+	if (relopts)
+		pfree(relopts);
+	if (tabentry)
+		pfree(tabentry);
+}
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *		Returns the autovacuum priority score for each relation in the
+ *		current database.
+ *
+ *		This follows the same setup as do_autovacuum(). Global state such
+ *		as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ *		computed here, while compute_autovac_score() handles the per-relation
+ *		score computation.
+ */
+#define NUM_AV_SCORE_COLS 10
+
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TableScanDesc relScan;
+	HeapTuple	tuple;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		compute_autovac_score(tuple, pg_class_desc,
+							  effective_multixact_freeze_max_age,
+							  &dovacuum, &doanalyze, &wraparound, &scores);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(dovacuum);
+		values[2] = BoolGetDatum(doanalyze);
+		values[3] = BoolGetDatum(wraparound);
+		values[4] = Float8GetDatum(scores.max);
+		values[5] = Float8GetDatum(scores.xid);
+		values[6] = Float8GetDatum(scores.mxid);
+		values[7] = Float8GetDatum(scores.vac);
+		values[8] = Float8GetDatum(scores.vac_ins);
+		values[9] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4d9ccc5789c..bce64758823 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202603241
+#define CATALOG_VERSION_NO	202603231
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0118e970dda..91d47617b0b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,15 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,xid_score,mxid_score,vacuum_dead_score,vacuum_ins_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..d2d3c3dd048 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.xid_score,
+    s.mxid_score,
+    s.vacuum_dead_score,
+    s.vacuum_ins_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, xid_score, mxid_score, vacuum_dead_score, vacuum_ins_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..0d7d1d87c0b 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,33 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Only xid_score and mxid_score
+-- are not tested, as they need to consume enough XID's.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AS score,
+  vacuum_dead_score > 0 AS vacuum_dead_score,
+  vacuum_ins_score > 0 AS vacuum_ins_score,
+  analyze_score > 0 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_dead_score | vacuum_ins_score | analyze_score 
+--------------+---------------+-------+-------------------+------------------+---------------
+ t            | t             | t     | t                 | t                | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..7a50858eeea 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,24 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Only xid_score and mxid_score
+-- are not tested, as they need to consume enough XID's.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AS score,
+  vacuum_dead_score > 0 AS vacuum_dead_score,
+  vacuum_ins_score > 0 AS vacuum_ins_score,
+  analyze_score > 0 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
\ No newline at end of file
-- 
2.47.3



  [application/octet-stream] v1-0001-Add-force_scores-option-to-relation_needs_vacanal.patch (7.0K, 3-v1-0001-Add-force_scores-option-to-relation_needs_vacanal.patch)
  download | inline diff:
From 85e99681cc7207b8e62a0ba8e604fde36bd5f6e4 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Fri, 27 Mar 2026 20:38:26 +0000
Subject: [PATCH v1 1/2] Add force_scores option to relation_needs_vacanalyze

A future commit will need the ability for relation_needs_vacanalyze()
to forcefully calculate an autovacuum priority score even if autovacuum
would otherwise abandon the computation of the score. This is needed for
tools that wish to introspect the score outside of the normal path of
do_autovacuum().
---
 src/backend/postmaster/autovacuum.c | 81 +++++++++++++++++------------
 1 file changed, 48 insertions(+), 33 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index d695f1de4bd..f6b213852e3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -378,6 +378,7 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
+									  bool force_scores,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
 									  AutoVacuumScores *scores);
 
@@ -2075,6 +2076,7 @@ do_autovacuum(void)
 		/* Check if it needs vacuum or analyze */
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
+								  false,
 								  &dovacuum, &doanalyze, &wraparound,
 								  &scores);
 
@@ -2175,6 +2177,7 @@ do_autovacuum(void)
 
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
+								  false,
 								  &dovacuum, &doanalyze, &wraparound,
 								  &scores);
 
@@ -2993,6 +2996,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
+							  false,
 							  dovacuum, doanalyze, wraparound,
 							  &scores);
 
@@ -3080,6 +3084,9 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * The autovacuum table score is returned in scores->max.  The component scores
  * are also returned in the "scores" argument via the other members of the
  * AutoVacuumScores struct.
+ *
+ * force_scores set to true forces the computation of a score. This is useful for
+ * tools that wish to inspect scores outside of the do_vacuum() path.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3087,6 +3094,7 @@ relation_needs_vacanalyze(Oid relid,
 						  Form_pg_class classForm,
 						  PgStat_StatTabEntry *tabentry,
 						  int effective_multixact_freeze_max_age,
+						  bool force_scores,
  /* output params below */
 						  bool *dovacuum,
 						  bool *doanalyze,
@@ -3252,18 +3260,21 @@ relation_needs_vacanalyze(Oid relid,
 		*dovacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
+	/*
+	 * User disabled it in pg_class.reloptions?  (But ignore if at risk or
+	 * forced)
+	 */
+	if (!force_scores && !av_enabled && !force_vacuum)
 		return;
 
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, and autovacuum is currently enabled
+	 * (or scores are forced), make a threshold-based decision whether to
+	 * vacuum and/or analyze.  If autovacuum is currently disabled, we must be
+	 * here for anti-wraparound vacuuming only, so don't vacuum (or analyze)
+	 * anything that's not being forced.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry && (AutoVacuumingActive() || force_scores))
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,32 +3315,33 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Update the vacuum score and determine if this table needs vacuum
 		 */
+		scores->vac = (double) vactuples / Max(vacthresh, 1);
+		scores->vac *= autovacuum_vacuum_score_weight;
+		scores->max = Max(scores->max, scores->vac);
 		if (vactuples > vacthresh)
-		{
-			scores->vac = (double) vactuples / Max(vacthresh, 1);
-			scores->vac *= autovacuum_vacuum_score_weight;
-			scores->max = Max(scores->max, scores->vac);
 			*dovacuum = true;
-		}
 
-		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+		/*
+		 * Ditto for insert vacuum
+		 */
+		if (vac_ins_base_thresh >= 0)
 		{
 			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
 		}
+		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+			*dovacuum = true;
 
 		/*
 		 * Determine if this table needs analyze, and update the score if it
 		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
 		 */
-		if (anltuples > anlthresh &&
-			relid != StatisticRelationId &&
-			classForm->relkind != RELKIND_TOASTVALUE)
+		if ((anltuples > anlthresh &&
+			 relid != StatisticRelationId &&
+			 classForm->relkind != RELKIND_TOASTVALUE))
 		{
 			scores->anl = (double) anltuples / Max(anlthresh, 1);
 			scores->anl *= autovacuum_analyze_score_weight;
@@ -3337,19 +3349,22 @@ relation_needs_vacanalyze(Oid relid,
 			*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+		if (!force_scores)
+		{
+			if (vac_ins_base_thresh >= 0)
+				elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 instuples, vacinsthresh, scores->vac_ins,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+			else
+				elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+		}
 	}
 }
 
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-28 04:14 ` SATYANARAYANA NARLAPURAM <[email protected]>
  2026-03-28 04:19   ` Re: Add pg_stat_autovacuum_priority SATYANARAYANA NARLAPURAM <[email protected]>
  2 siblings, 1 reply; 60+ messages in thread

From: SATYANARAYANA NARLAPURAM @ 2026-03-28 04:14 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: pgsql-hackers

Hi Sami,


On Fri, Mar 27, 2026 at 4:14 PM Sami Imseih <[email protected]> wrote:

> Hi,
>
> This is a quick follow-up to the commit d7965d65f which
> introduced autovacuum prioritization based on a score that
> is the Max of several components, such as vacuum
> thresholds, xid age, etc.
>
> It was also discussed in that thread [1] that we will need
> a view to expose the priority scores, per table in a view.
> This will allow a user to introspect what the autovacuum
> launcher will prioritize next as well as verify tuning
> efforts for autovacuum prioritization; the latter case
> likely being rare.
>
> So after spending time on this today, I am proposing a view
> that returns a line for each relation with information
> about if the table needs autovacuum/autoanalyze, as well as
> scores of each component and the Max score. It looks like
> the below:
>
> ```
> postgres=# select * FROM pg_stat_autovacuum_priority;
> -[ RECORD 1 ]-----+----------------------------
> relid             | 16410
> schemaname        | public
> relname           | av_priority_test
> needs_vacuum      | f
> needs_analyze     | f
> wraparound        | f
> score             | 0
> xid_score         | 0
> mxid_score        | 0
> vacuum_dead_score | 0
> vacuum_ins_score  | 0
> analyze_score     | 0
> ```
>
> The function essentially calls relation_needs_vacanalyze()
> with some setup work, such as scanning the catalog with an
> AccessShareLock, etc. and emits the result of this call.
>
> To make this work 0001 introduces a small change to
> relation_needs_vacanalyze() to take in a boolean to force
> the calculation of the score (even if autovacuum is
> disabled for the relation).
>
> 0002 introduces the view with documentation and testing in
> vacuum.c (xid age and mxid age scores are not tested as
> they require xid_wraparound to consume enough XIDs to
> trigger a score, which will cost too much time for a
> regression test).
>
> Find the attached taking the first attempt at this view.
>
> [1] [
> https://www.postgresql.org/message-id/CAApHDvqQN-B2sQov8nsfZOmx-VeJMauSf4kLa3A8LsK1tUyBNw%40mail.gma...
> ]
>


Thanks for adding this. Applied the patch and the tests passed. I haven't
fully reviewed the patch but have a few comments below:

1. Please ass CFI in the function pg_stat_get_autovacuum_priority, as the
list of tables can be very long

+ while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+ {

2. Should we add filtering? The current approach
pg_stat_get_autovacuum_priority does a full catalog scan without any
filters and can be expensive.

3. Please add tests for tables with autovacuum = off

4. Is the view intended to be exposed to PUBLIC without any ACL
restrictions?

5. Catalog version number needs to be increased

-#define CATALOG_VERSION_NO 202603241
+#define CATALOG_VERSION_NO 202603231

Thanks,
Satya


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 04:14 ` Re: Add pg_stat_autovacuum_priority SATYANARAYANA NARLAPURAM <[email protected]>
@ 2026-03-28 04:19   ` SATYANARAYANA NARLAPURAM <[email protected]>
  0 siblings, 0 replies; 60+ messages in thread

From: SATYANARAYANA NARLAPURAM @ 2026-03-28 04:19 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: pgsql-hackers

On Fri, Mar 27, 2026 at 9:14 PM SATYANARAYANA NARLAPURAM <
[email protected]> wrote:

>
> Hi Sami,
>
>
> On Fri, Mar 27, 2026 at 4:14 PM Sami Imseih <[email protected]> wrote:
>
>> Hi,
>>
>> This is a quick follow-up to the commit d7965d65f which
>> introduced autovacuum prioritization based on a score that
>> is the Max of several components, such as vacuum
>> thresholds, xid age, etc.
>>
>> It was also discussed in that thread [1] that we will need
>> a view to expose the priority scores, per table in a view.
>> This will allow a user to introspect what the autovacuum
>> launcher will prioritize next as well as verify tuning
>> efforts for autovacuum prioritization; the latter case
>> likely being rare.
>>
>> So after spending time on this today, I am proposing a view
>> that returns a line for each relation with information
>> about if the table needs autovacuum/autoanalyze, as well as
>> scores of each component and the Max score. It looks like
>> the below:
>>
>> ```
>> postgres=# select * FROM pg_stat_autovacuum_priority;
>> -[ RECORD 1 ]-----+----------------------------
>> relid             | 16410
>> schemaname        | public
>> relname           | av_priority_test
>> needs_vacuum      | f
>> needs_analyze     | f
>> wraparound        | f
>> score             | 0
>> xid_score         | 0
>> mxid_score        | 0
>> vacuum_dead_score | 0
>> vacuum_ins_score  | 0
>> analyze_score     | 0
>> ```
>>
>> The function essentially calls relation_needs_vacanalyze()
>> with some setup work, such as scanning the catalog with an
>> AccessShareLock, etc. and emits the result of this call.
>>
>> To make this work 0001 introduces a small change to
>> relation_needs_vacanalyze() to take in a boolean to force
>> the calculation of the score (even if autovacuum is
>> disabled for the relation).
>>
>> 0002 introduces the view with documentation and testing in
>> vacuum.c (xid age and mxid age scores are not tested as
>> they require xid_wraparound to consume enough XIDs to
>> trigger a score, which will cost too much time for a
>> regression test).
>>
>> Find the attached taking the first attempt at this view.
>>
>> [1] [
>> https://www.postgresql.org/message-id/CAApHDvqQN-B2sQov8nsfZOmx-VeJMauSf4kLa3A8LsK1tUyBNw%40mail.gma...
>> ]
>>
>
>
> Thanks for adding this. Applied the patch and the tests passed. I haven't
> fully reviewed the patch but have a few comments below:
>
> 1. Please ass CFI in the function pg_stat_get_autovacuum_priority, as the
> list of tables can be very long
>
> + while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
> + {
>
> 2. Should we add filtering? The current approach
> pg_stat_get_autovacuum_priority does a full catalog scan without any
> filters and can be expensive.
>
> 3. Please add tests for tables with autovacuum = off
>
> 4. Is the view intended to be exposed to PUBLIC without any ACL
> restrictions?
>
> 5. Catalog version number needs to be increased
>
> -#define CATALOG_VERSION_NO 202603241
> +#define CATALOG_VERSION_NO 202603231
>

Additionally, do you expect this view to be available on the hot_Standby?
Because on a hot standby, the view only provides useful wraparound risk
data. All activity-based columns are blind. This should either be
documented, or the function should check RecoveryInProgress() and raise an
error/notice

Thanks,
Satya

>


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-28 09:18 ` Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2 siblings, 1 reply; 60+ messages in thread

From: Bharath Rupireddy @ 2026-03-28 09:18 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: pgsql-hackers

Hi,

On Fri, Mar 27, 2026 at 4:14 PM Sami Imseih <[email protected]> wrote:
>
> This is a quick follow-up to the commit d7965d65f which
> introduced autovacuum prioritization based on a score that
> is the Max of several components, such as vacuum
> thresholds, xid age, etc.

Thanks for sending the patch.

+1 for the visibility into the new autovacuum scoring system and its
impact on prioritization. It would also be nice to add the computed
scores to pg_stat_progress_vacuum to show the current prioritization.
IMHO, we can get that change first. It's relatively smaller.

I quickly reviewed the v1 patches. Here are some comments:

1/
+ * pg_stat_get_autovacuum_priority
+ * Returns the autovacuum priority score for each relation in the
+ * current database.
+ *
Can we have the per-relation prioritization computation function in C
and provide a per-database computation function as a SQL function over
this per-relation function in system_functions.sql? This would
simplify things and also address the concern raised by Satya in this
thread about databases having a large number of relations. Would that
work?

2/ Do we need to revoke permissions on pg_stat_get_autovacuum_priority
for all and grant them to pg_monitor or similar? Especially since this
function loops over all the relations in a database, we may not want
everyone to be able to do this.

--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
@ 2026-03-28 17:53   ` Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-03-28 17:53 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; [email protected]; +Cc: pgsql-hackers

Hi,

Thank you all for the review and comments!

> 1. Please add CFI in the function pg_stat_get_autovacuum_priority, as the list of tables can be very long
good catch. Will add.


> 3. Please add tests for tables with autovacuum = off
Yes, I will add this test as well.


> 4. Is the view intended to be exposed to PUBLIC without any ACL restrictions?

> 2/ Do we need to revoke permissions on pg_stat_get_autovacuum_priority
> for all and grant them to pg_monitor or similar? Especially since this
> function loops over all the relations in a database, we may not want
> everyone to be able to do this.

I think you're correct there. While the data is not sensitive, it
should have more controlled usage. It's only taking an AccessShareLock,
but you would not want anyone to be able to run this since it's
doing real computation. I think requiring pg_read_all_stats
is a good idea. Will do.


> 5. Catalog version number needs to be increased
This will be left to the committer at the time.

> 2. Should we add filtering? The current approach pg_stat_get_autovacuum_priority
> does a full catalog scan without any filters and can be expensive.

> Can we have the per-relation prioritization computation function in C
> and provide a per-database computation function as a SQL function over
> this per-relation function in system_functions.sql?

Yes, perhaps we should do this. So we can have a function called
pg_stat_get_autovacuum_priority() that either takes a NULL or an OID
to either return all the tables or just a single table.
This is a similar usage pattern as pg_stat_get_subscription or
pg_stat_get_activity.

pg_stat_autovacuum_priority will be a view that wraps around the NULL
variant of the function.

The case where the OID is passed we just do a SearchSysCache1(RELOID,...)
whereas the other case will do the full catalog scan.

What do you think?

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-30 02:09     ` Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Bharath Rupireddy @ 2026-03-30 02:09 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: [email protected]; pgsql-hackers

Hi,

On Sat, Mar 28, 2026 at 10:54 AM Sami Imseih <[email protected]> wrote:
>
> > 4. Is the view intended to be exposed to PUBLIC without any ACL restrictions?
>
> > 2/ Do we need to revoke permissions on pg_stat_get_autovacuum_priority
> > for all and grant them to pg_monitor or similar? Especially since this
> > function loops over all the relations in a database, we may not want
> > everyone to be able to do this.
>
> I think you're correct there. While the data is not sensitive, it
> should have more controlled usage. It's only taking an AccessShareLock,
> but you would not want anyone to be able to run this since it's
> doing real computation. I think requiring pg_read_all_stats
> is a good idea. Will do.

+1 for pg_read_all_stats.

> > Can we have the per-relation prioritization computation function in C
> > and provide a per-database computation function as a SQL function over
> > this per-relation function in system_functions.sql?
>
> Yes, perhaps we should do this. So we can have a function called
> pg_stat_get_autovacuum_priority() that either takes a NULL or an OID
> to either return all the tables or just a single table.
> This is a similar usage pattern as pg_stat_get_subscription or
> pg_stat_get_activity.
>
> pg_stat_autovacuum_priority will be a view that wraps around the NULL
> variant of the function.
>
> The case where the OID is passed we just do a SearchSysCache1(RELOID,...)
> whereas the other case will do the full catalog scan.
>
> What do you think?

IMHO, we can have pg_stat_get_relation_autovacuum_priority defined as
a C function to give the autovacuum scoring as of the given moment for
the given table. It's easy for one to write a function to get scoring
for all the relations in a database. This keeps things simple yet
useful.

--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
@ 2026-03-30 12:56       ` Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Robert Treat @ 2026-03-30 12:56 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; +Cc: Sami Imseih <[email protected]>; [email protected]; pgsql-hackers

On Sun, Mar 29, 2026 at 10:09 PM Bharath Rupireddy
<[email protected]> wrote:
> On Sat, Mar 28, 2026 at 10:54 AM Sami Imseih <[email protected]> wrote:
> >
> > > 4. Is the view intended to be exposed to PUBLIC without any ACL restrictions?
> >
> > > 2/ Do we need to revoke permissions on pg_stat_get_autovacuum_priority
> > > for all and grant them to pg_monitor or similar? Especially since this
> > > function loops over all the relations in a database, we may not want
> > > everyone to be able to do this.
> >
> > I think you're correct there. While the data is not sensitive, it
> > should have more controlled usage. It's only taking an AccessShareLock,
> > but you would not want anyone to be able to run this since it's
> > doing real computation. I think requiring pg_read_all_stats
> > is a good idea. Will do.
>
> +1 for pg_read_all_stats.
>

Is there a gap here where someone may have been granted MAINTAIN on a
relation but they do not have pg_read_all_stats?

> > > Can we have the per-relation prioritization computation function in C
> > > and provide a per-database computation function as a SQL function over
> > > this per-relation function in system_functions.sql?
> >
> > Yes, perhaps we should do this. So we can have a function called
> > pg_stat_get_autovacuum_priority() that either takes a NULL or an OID
> > to either return all the tables or just a single table.
> > This is a similar usage pattern as pg_stat_get_subscription or
> > pg_stat_get_activity.
> >
> > pg_stat_autovacuum_priority will be a view that wraps around the NULL
> > variant of the function.
> >
> > The case where the OID is passed we just do a SearchSysCache1(RELOID,...)
> > whereas the other case will do the full catalog scan.
> >
> > What do you think?
>
> IMHO, we can have pg_stat_get_relation_autovacuum_priority defined as
> a C function to give the autovacuum scoring as of the given moment for
> the given table. It's easy for one to write a function to get scoring
> for all the relations in a database. This keeps things simple yet
> useful.
>

I don't have a strong opinion on the above, but I do suspect that the
most common way people will interact with this is by querying against
the view with a WHERE clause, so optimizing for that case seems
important.


Robert Treat
https://xzilla.net





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
@ 2026-03-30 15:16         ` Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-03-30 15:16 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

> On Sun, Mar 29, 2026 at 10:09 PM Bharath Rupireddy
> <[email protected]> wrote:
> > On Sat, Mar 28, 2026 at 10:54 AM Sami Imseih <[email protected]> wrote:
> > >
> > > > 4. Is the view intended to be exposed to PUBLIC without any ACL restrictions?
> > >
> > > > 2/ Do we need to revoke permissions on pg_stat_get_autovacuum_priority
> > > > for all and grant them to pg_monitor or similar? Especially since this
> > > > function loops over all the relations in a database, we may not want
> > > > everyone to be able to do this.
> > >
> > > I think you're correct there. While the data is not sensitive, it
> > > should have more controlled usage. It's only taking an AccessShareLock,
> > > but you would not want anyone to be able to run this since it's
> > > doing real computation. I think requiring pg_read_all_stats
> > > is a good idea. Will do.
> >
> > +1 for pg_read_all_stats.
> >
>
> Is there a gap here where someone may have been granted MAINTAIN on a
> relation but they do not have pg_read_all_stats?

Yes, that is possible. MAINTAIN is a per-object privilege granted on a relation,
whereas pg_read_all_stats is a global role membership. They operate at
different levels.

I don't think one needs to have MAINTAIN permissions on the table to see the
autovacuum score. DBA Monitoring users are usually separate from the DBA
operational users.

I think pg_read_all_stats is the right permission here and it should
be implemented
similar to how pg_get_shmem_allocations is done where the default permissions
are pg_read_all_stats. pg_monitor inherits pg_read_all_stats so any
user with this
privilege will be able to access this view. A DBA is free to also add
privileges to
to other users if they wish.

This is unlike other pg_stat_* views that have tuple level permission
checks ( i.e.
pg_stat_activity), but in those cases the permissions are needed to
hide sensitive data.
This is not the case here.

> > IMHO, we can have pg_stat_get_relation_autovacuum_priority defined as
> > a C function to give the autovacuum scoring as of the given moment for
> > the given table. It's easy for one to write a function to get scoring
> > for all the relations in a database. This keeps things simple yet
> > useful.
> >
>
> I don't have a strong opinion on the above, but I do suspect that the
> most common way people will interact with this is by querying against
> the view with a WHERE clause, so optimizing for that case seems
> important.

Yeah, after sleeping on it I actually think the most common case will likely be
ORDER BY score DESC LIMIT ... because you usually want to see where your
table priority is relative to everything else in the database.
For the rare case where someone wants to look up an individual table, the caller
can just use a WHERE clause. So, we should just always do the full pg_class
scan. I don't see why we need to complicate the c-function more than this.

Attached v2 implements it as above.

A few other things in v2:

1/ I set autovacuum_enabled = OFF in the tests. This will make
sure the test is both stable and will also test that the score is
returned even in
the case where autovacuum is disabled.

2/ Moved pg_stat_autovacuum_priority to the end of the docs in
"Monitoring Database Activity".

3/ Also added a mention of the extremely high score values when failsafe
is triggered [1]

[1] https://www.postgresql.org/message-id/abGP87A3JPIXDG2I%40nathan

--
Sami


Attachments:

  [application/octet-stream] v2-0002-Add-pg_stat_autovacuum_priority-view.patch (19.7K, 2-v2-0002-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From b2c317a24e35d41587797c7dd60ba7cadecca541 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v2 2/2] Add pg_stat_autovacuum_priority view

d7965d65f introduced autovacuum prioritization with
scoring. This change adds a view to expose those scores.

The view shows a row per relation indicating whether it
needs vacuum or analyze, the score of each component
and the Max score across all components. This provides
a way to introspect autovacuum priority and provide
feedback on tuning of the related GUCs.

The underlying function pg_stat_get_autovacuum_priority()
scans all relations in the current database and computes
scores using relation_needs_vacanalyze(). By default,
only superusers and roles with privileges of
pg_read_all_stats can execute the function, as
controlled by the function's ACL in pg_proc.

The view also emits the relid, namespace and relname,
so it can be joined with other views like
pg_stat_all_tables and pg_stat_progress_vacuum for
complementary vacuum details.

Tests added to vacuum.sql

Discussion: https://www.postgresql.org/message-id/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 166 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  21 ++++
 src/backend/postmaster/autovacuum.c  | 112 ++++++++++++++++++
 src/include/catalog/pg_proc.dat      |  10 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  33 ++++++
 src/test/regress/sql/vacuum.sql      |  24 ++++
 8 files changed, 387 insertions(+)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..8ace8d4a9b0 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -5256,6 +5265,163 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority. The
+   <structfield>score</structfield>, <structfield>xid_score</structfield>,
+   and <structfield>mxid_score</structfield> values may be very large for
+   tables approaching wraparound, as these scores are scaled aggressively
+   once they surpass the failsafe age thresholds.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if autovacuum considers this table in need of vacuuming
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if autovacuum considers this table in need of analyzing
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>xid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>mxid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_dead_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_ins_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_stat_autovacuum_priority</structname> view can
+   be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+
+ </sect2>
+
  <sect2 id="monitoring-stats-functions">
   <title>Statistics Functions</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..55e08e20d80 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,27 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.xid_score,
+            S.mxid_score,
+            S.vacuum_dead_score,
+            S.vacuum_ins_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
+REVOKE ALL ON pg_stat_autovacuum_priority FROM PUBLIC;
+GRANT SELECT ON pg_stat_autovacuum_priority TO pg_read_all_stats;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f6b213852e3..44be986e5ac 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -390,6 +392,10 @@ static void perform_work_item(AutoVacuumWorkItem *workitem);
 static void autovac_report_activity(autovac_table *tab);
 static void autovac_report_workitem(AutoVacuumWorkItem *workitem,
 									const char *nspname, const char *relname);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumScores *scores);
 static void avl_sigusr2_handler(SIGNAL_ARGS);
 static bool av_worker_available(void);
 static void check_av_worker_gucs(void);
@@ -3679,3 +3685,109 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * compute_autovac_score
+ *		Wrapper around relation_needs_vacanalyze() that handles the
+ *		per-relation setup similar to do_autovacuum() before calling
+ *		relation_needs_vacanalyze().
+ */
+static void
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumScores *scores)
+{
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+	AutoVacOpts *relopts;
+	PgStat_StatTabEntry *tabentry;
+
+	relopts = extract_autovac_opts(tuple, pg_class_desc);
+
+	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
+											  classForm->oid);
+
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
+							  effective_multixact_freeze_max_age, true,
+							  dovacuum, doanalyze, wraparound, scores);
+
+	if (relopts)
+		pfree(relopts);
+	if (tabentry)
+		pfree(tabentry);
+}
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *		Returns the autovacuum priority score for all eligible relations
+ *		in the current database.
+ *
+ *		This follows the same setup as do_autovacuum(). Global state such
+ *		as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ *		computed here, while compute_autovac_score() handles the per-relation
+ *		score computation.
+ */
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+#define NUM_AV_SCORE_COLS 10
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+	TableScanDesc relScan;
+	HeapTuple	tuple;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		compute_autovac_score(tuple, pg_class_desc,
+							  effective_multixact_freeze_max_age,
+							  &dovacuum, &doanalyze, &wraparound, &scores);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(dovacuum);
+		values[2] = BoolGetDatum(doanalyze);
+		values[3] = BoolGetDatum(wraparound);
+		values[4] = Float8GetDatum(scores.max);
+		values[5] = Float8GetDatum(scores.xid);
+		values[6] = Float8GetDatum(scores.mxid);
+		values[7] = Float8GetDatum(scores.vac);
+		values[8] = Float8GetDatum(scores.vac_ins);
+		values[9] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..6268f8c6018 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,16 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,xid_score,mxid_score,vacuum_dead_score,vacuum_ins_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority',
+  proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..d2d3c3dd048 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.xid_score,
+    s.mxid_score,
+    s.vacuum_dead_score,
+    s.vacuum_ins_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, xid_score, mxid_score, vacuum_dead_score, vacuum_ins_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..e8c1fc8d152 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,36 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Only xid_score and mxid_score
+-- are not tested, as they need to consume enough XID's. The test is
+-- run with autovacuum disabled for test stability as well as ensuring
+-- that priority fields are still populated in this case.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AS score,
+  vacuum_dead_score > 0 AS vacuum_dead_score,
+  vacuum_ins_score > 0 AS vacuum_ins_score,
+  analyze_score > 0 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_dead_score | vacuum_ins_score | analyze_score 
+--------------+---------------+-------+-------------------+------------------+---------------
+ t            | t             | t     | t                 | t                | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..ba373fa31c6 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,27 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Only xid_score and mxid_score
+-- are not tested, as they need to consume enough XID's. The test is
+-- run with autovacuum disabled for test stability as well as ensuring
+-- that priority fields are still populated in this case.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AS score,
+  vacuum_dead_score > 0 AS vacuum_dead_score,
+  vacuum_ins_score > 0 AS vacuum_ins_score,
+  analyze_score > 0 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
\ No newline at end of file
-- 
2.47.3



  [application/octet-stream] v2-0001-Add-force_scores-option-to-relation_needs_vacanal.patch (7.0K, 3-v2-0001-Add-force_scores-option-to-relation_needs_vacanal.patch)
  download | inline diff:
From 9e09eae2dd17fb8b88f9b444d61aaab791c57c16 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Fri, 27 Mar 2026 20:38:26 +0000
Subject: [PATCH v2 1/2] Add force_scores option to relation_needs_vacanalyze

A future commit will need the ability for relation_needs_vacanalyze()
to forcefully calculate an autovacuum priority score even if autovacuum
would otherwise abandon the computation of the score. This is needed for
tools that wish to introspect the score outside of the normal path of
do_autovacuum().
---
 src/backend/postmaster/autovacuum.c | 81 +++++++++++++++++------------
 1 file changed, 48 insertions(+), 33 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index d695f1de4bd..f6b213852e3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -378,6 +378,7 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
+									  bool force_scores,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
 									  AutoVacuumScores *scores);
 
@@ -2075,6 +2076,7 @@ do_autovacuum(void)
 		/* Check if it needs vacuum or analyze */
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
+								  false,
 								  &dovacuum, &doanalyze, &wraparound,
 								  &scores);
 
@@ -2175,6 +2177,7 @@ do_autovacuum(void)
 
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
+								  false,
 								  &dovacuum, &doanalyze, &wraparound,
 								  &scores);
 
@@ -2993,6 +2996,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
+							  false,
 							  dovacuum, doanalyze, wraparound,
 							  &scores);
 
@@ -3080,6 +3084,9 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * The autovacuum table score is returned in scores->max.  The component scores
  * are also returned in the "scores" argument via the other members of the
  * AutoVacuumScores struct.
+ *
+ * force_scores set to true forces the computation of a score. This is useful for
+ * tools that wish to inspect scores outside of the do_vacuum() path.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3087,6 +3094,7 @@ relation_needs_vacanalyze(Oid relid,
 						  Form_pg_class classForm,
 						  PgStat_StatTabEntry *tabentry,
 						  int effective_multixact_freeze_max_age,
+						  bool force_scores,
  /* output params below */
 						  bool *dovacuum,
 						  bool *doanalyze,
@@ -3252,18 +3260,21 @@ relation_needs_vacanalyze(Oid relid,
 		*dovacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
+	/*
+	 * User disabled it in pg_class.reloptions?  (But ignore if at risk or
+	 * forced)
+	 */
+	if (!force_scores && !av_enabled && !force_vacuum)
 		return;
 
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, and autovacuum is currently enabled
+	 * (or scores are forced), make a threshold-based decision whether to
+	 * vacuum and/or analyze.  If autovacuum is currently disabled, we must be
+	 * here for anti-wraparound vacuuming only, so don't vacuum (or analyze)
+	 * anything that's not being forced.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry && (AutoVacuumingActive() || force_scores))
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,32 +3315,33 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Update the vacuum score and determine if this table needs vacuum
 		 */
+		scores->vac = (double) vactuples / Max(vacthresh, 1);
+		scores->vac *= autovacuum_vacuum_score_weight;
+		scores->max = Max(scores->max, scores->vac);
 		if (vactuples > vacthresh)
-		{
-			scores->vac = (double) vactuples / Max(vacthresh, 1);
-			scores->vac *= autovacuum_vacuum_score_weight;
-			scores->max = Max(scores->max, scores->vac);
 			*dovacuum = true;
-		}
 
-		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+		/*
+		 * Ditto for insert vacuum
+		 */
+		if (vac_ins_base_thresh >= 0)
 		{
 			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
 		}
+		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+			*dovacuum = true;
 
 		/*
 		 * Determine if this table needs analyze, and update the score if it
 		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
 		 */
-		if (anltuples > anlthresh &&
-			relid != StatisticRelationId &&
-			classForm->relkind != RELKIND_TOASTVALUE)
+		if ((anltuples > anlthresh &&
+			 relid != StatisticRelationId &&
+			 classForm->relkind != RELKIND_TOASTVALUE))
 		{
 			scores->anl = (double) anltuples / Max(anlthresh, 1);
 			scores->anl *= autovacuum_analyze_score_weight;
@@ -3337,19 +3349,22 @@ relation_needs_vacanalyze(Oid relid,
 			*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+		if (!force_scores)
+		{
+			if (vac_ins_base_thresh >= 0)
+				elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 instuples, vacinsthresh, scores->vac_ins,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+			else
+				elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+		}
 	}
 }
 
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-30 17:13           ` Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Robert Treat @ 2026-03-30 17:13 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

On Mon, Mar 30, 2026 at 11:17 AM Sami Imseih <[email protected]> wrote:
> > On Sun, Mar 29, 2026 at 10:09 PM Bharath Rupireddy
> > <[email protected]> wrote:
> > > On Sat, Mar 28, 2026 at 10:54 AM Sami Imseih <[email protected]> wrote:
> > > >
> > > > > 4. Is the view intended to be exposed to PUBLIC without any ACL restrictions?
> > > >
> > > > > 2/ Do we need to revoke permissions on pg_stat_get_autovacuum_priority
> > > > > for all and grant them to pg_monitor or similar? Especially since this
> > > > > function loops over all the relations in a database, we may not want
> > > > > everyone to be able to do this.
> > > >
> > > > I think you're correct there. While the data is not sensitive, it
> > > > should have more controlled usage. It's only taking an AccessShareLock,
> > > > but you would not want anyone to be able to run this since it's
> > > > doing real computation. I think requiring pg_read_all_stats
> > > > is a good idea. Will do.
> > >
> > > +1 for pg_read_all_stats.
> > >
> >
> > Is there a gap here where someone may have been granted MAINTAIN on a
> > relation but they do not have pg_read_all_stats?
>
> Yes, that is possible. MAINTAIN is a per-object privilege granted on a relation,
> whereas pg_read_all_stats is a global role membership. They operate at
> different levels.
>
> I don't think one needs to have MAINTAIN permissions on the table to see the
> autovacuum score. DBA Monitoring users are usually separate from the DBA
> operational users.
>
> I think pg_read_all_stats is the right permission here and it should
> be implemented
> similar to how pg_get_shmem_allocations is done where the default permissions
> are pg_read_all_stats. pg_monitor inherits pg_read_all_stats so any
> user with this
> privilege will be able to access this view. A DBA is free to also add
> privileges to
> to other users if they wish.
>
> This is unlike other pg_stat_* views that have tuple level permission
> checks ( i.e.
> pg_stat_activity), but in those cases the permissions are needed to
> hide sensitive data.
> This is not the case here.
>

I don't think we are in disagreement here, I was just thinking about
it the other way round; someone might have MAINTAIN privileges on a
table and want to see what the relevant "autovacuum score" is before
taking action. If the solution for that is to give those roles
pg_read_all_stats, I guess that's ok, but there was probably a reason
the permissions were limited in the first place. *shrug*

> > > IMHO, we can have pg_stat_get_relation_autovacuum_priority defined as
> > > a C function to give the autovacuum scoring as of the given moment for
> > > the given table. It's easy for one to write a function to get scoring
> > > for all the relations in a database. This keeps things simple yet
> > > useful.
> > >
> >
> > I don't have a strong opinion on the above, but I do suspect that the
> > most common way people will interact with this is by querying against
> > the view with a WHERE clause, so optimizing for that case seems
> > important.
>
> Yeah, after sleeping on it I actually think the most common case will likely be
> ORDER BY score DESC LIMIT ... because you usually want to see where your
> table priority is relative to everything else in the database.
> For the rare case where someone wants to look up an individual table, the caller
> can just use a WHERE clause. So, we should just always do the full pg_class
> scan. I don't see why we need to complicate the c-function more than this.
>

I think we are also in agreement here, although based on my
experience, filtering out things like system and toast tables will be
common, but I don't see that changing what you said above.  On a
similar note, +1 to your changes in v2.


Robert Treat
https://xzilla.net





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
@ 2026-03-30 18:16             ` Sami Imseih <[email protected]>
  2026-03-31 15:38               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  0 siblings, 2 replies; 60+ messages in thread

From: Sami Imseih @ 2026-03-30 18:16 UTC (permalink / raw)
  To: Robert Treat <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

> I think we are also in agreement here, although based on my
> experience, filtering out things like system and toast tables will be
> common, but I don't see that changing what you said above.  On a
> similar note, +1 to your changes in v2.

Thanks!

v3 now includes the refactoring [1] suggestion brought up by Alvarro


[1] [https://www.postgresql.org/message-id/202603301508.up22nvhgnnoj%40alvherre.pgsql]

--
Sami


Attachments:

  [application/octet-stream] v3-0002-Refactor-autovacuum-score-computation-into-comput.patch (4.4K, 2-v3-0002-Refactor-autovacuum-score-computation-into-comput.patch)
  download | inline diff:
From 2940d2a1b497ea6791918a6646690779c9b01065 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 30 Mar 2026 17:23:09 +0000
Subject: [PATCH v3 2/3] Refactor autovacuum score computation into
 compute_autovac_score

The pattern of fetching a relation's pgstat entry and
calling relation_needs_vacanalyze() is needed in multiple
places: table_recheck_autovac and a future view that will
emit a relation's autovacuum priority score.

Introduce compute_autovac_score() to consolidate this.
It accepts relopts and force_scores parameters, and
replaces recheck_relation_needs_vacanalyze() which did
the same thing with a narrower interface.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 51 ++++++++++++++---------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f6b213852e3..fbe670d375f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -370,10 +370,11 @@ static void FreeWorkerInfo(int code, Datum arg);
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 											TupleDesc pg_class_desc,
 											int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
-											  Form_pg_class classForm,
-											  int effective_multixact_freeze_max_age,
-											  bool *dovacuum, bool *doanalyze, bool *wraparound);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  AutoVacOpts *relopts, bool force_scores,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumScores *scores);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
@@ -2834,6 +2835,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	bool		wraparound;
 	AutoVacOpts *avopts;
 	bool		free_avopts = false;
+	AutoVacuumScores scores;
 
 	/* fetch the relation's relcache entry */
 	classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2859,9 +2861,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			avopts = &hentry->ar_reloptions;
 	}
 
-	recheck_relation_needs_vacanalyze(relid, avopts, classForm,
-									  effective_multixact_freeze_max_age,
-									  &dovacuum, &doanalyze, &wraparound);
+	compute_autovac_score(classTup, pg_class_desc,
+						  effective_multixact_freeze_max_age,
+						  avopts, false,
+						  &dovacuum, &doanalyze, &wraparound, &scores);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -2971,34 +2974,28 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 }
 
 /*
- * recheck_relation_needs_vacanalyze
- *
- * Subroutine for table_recheck_autovac.
- *
- * Fetch the pgstat of a relation and recheck whether a relation
- * needs to be vacuumed or analyzed.
+ * compute_autovac_score
+ *		Fetch the pgstat entry for a relation and call
+ *		relation_needs_vacanalyze() to determine whether it needs
+ *		vacuum or analyze and compute its priority scores.
  */
 static void
-recheck_relation_needs_vacanalyze(Oid relid,
-								  AutoVacOpts *avopts,
-								  Form_pg_class classForm,
-								  int effective_multixact_freeze_max_age,
-								  bool *dovacuum,
-								  bool *doanalyze,
-								  bool *wraparound)
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  AutoVacOpts *relopts, bool force_scores,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumScores *scores)
 {
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 	PgStat_StatTabEntry *tabentry;
-	AutoVacuumScores scores;
 
 	/* fetch the pgstat table entry */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
+											  classForm->oid);
 
-	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
-							  effective_multixact_freeze_max_age,
-							  false,
-							  dovacuum, doanalyze, wraparound,
-							  &scores);
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
+							  effective_multixact_freeze_max_age, force_scores,
+							  dovacuum, doanalyze, wraparound, scores);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
-- 
2.47.3



  [application/octet-stream] v3-0001-Add-force_scores-option-to-relation_needs_vacanal.patch (7.1K, 3-v3-0001-Add-force_scores-option-to-relation_needs_vacanal.patch)
  download | inline diff:
From 656fe90948206b7fd2b64a32006f701b85a84cae Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Fri, 27 Mar 2026 20:38:26 +0000
Subject: [PATCH v3 1/3] Add force_scores option to relation_needs_vacanalyze

A future commit will need the ability for relation_needs_vacanalyze()
to forcefully calculate an autovacuum priority score even if autovacuum
would otherwise abandon the computation of the score. This is needed for
tools that wish to introspect the score outside of the normal path of
do_autovacuum().

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 81 +++++++++++++++++------------
 1 file changed, 48 insertions(+), 33 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index d695f1de4bd..f6b213852e3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -378,6 +378,7 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
+									  bool force_scores,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
 									  AutoVacuumScores *scores);
 
@@ -2075,6 +2076,7 @@ do_autovacuum(void)
 		/* Check if it needs vacuum or analyze */
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
+								  false,
 								  &dovacuum, &doanalyze, &wraparound,
 								  &scores);
 
@@ -2175,6 +2177,7 @@ do_autovacuum(void)
 
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
+								  false,
 								  &dovacuum, &doanalyze, &wraparound,
 								  &scores);
 
@@ -2993,6 +2996,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
+							  false,
 							  dovacuum, doanalyze, wraparound,
 							  &scores);
 
@@ -3080,6 +3084,9 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * The autovacuum table score is returned in scores->max.  The component scores
  * are also returned in the "scores" argument via the other members of the
  * AutoVacuumScores struct.
+ *
+ * force_scores set to true forces the computation of a score. This is useful for
+ * tools that wish to inspect scores outside of the do_vacuum() path.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3087,6 +3094,7 @@ relation_needs_vacanalyze(Oid relid,
 						  Form_pg_class classForm,
 						  PgStat_StatTabEntry *tabentry,
 						  int effective_multixact_freeze_max_age,
+						  bool force_scores,
  /* output params below */
 						  bool *dovacuum,
 						  bool *doanalyze,
@@ -3252,18 +3260,21 @@ relation_needs_vacanalyze(Oid relid,
 		*dovacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
+	/*
+	 * User disabled it in pg_class.reloptions?  (But ignore if at risk or
+	 * forced)
+	 */
+	if (!force_scores && !av_enabled && !force_vacuum)
 		return;
 
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, and autovacuum is currently enabled
+	 * (or scores are forced), make a threshold-based decision whether to
+	 * vacuum and/or analyze.  If autovacuum is currently disabled, we must be
+	 * here for anti-wraparound vacuuming only, so don't vacuum (or analyze)
+	 * anything that's not being forced.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry && (AutoVacuumingActive() || force_scores))
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,32 +3315,33 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Update the vacuum score and determine if this table needs vacuum
 		 */
+		scores->vac = (double) vactuples / Max(vacthresh, 1);
+		scores->vac *= autovacuum_vacuum_score_weight;
+		scores->max = Max(scores->max, scores->vac);
 		if (vactuples > vacthresh)
-		{
-			scores->vac = (double) vactuples / Max(vacthresh, 1);
-			scores->vac *= autovacuum_vacuum_score_weight;
-			scores->max = Max(scores->max, scores->vac);
 			*dovacuum = true;
-		}
 
-		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+		/*
+		 * Ditto for insert vacuum
+		 */
+		if (vac_ins_base_thresh >= 0)
 		{
 			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
 		}
+		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+			*dovacuum = true;
 
 		/*
 		 * Determine if this table needs analyze, and update the score if it
 		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
 		 */
-		if (anltuples > anlthresh &&
-			relid != StatisticRelationId &&
-			classForm->relkind != RELKIND_TOASTVALUE)
+		if ((anltuples > anlthresh &&
+			 relid != StatisticRelationId &&
+			 classForm->relkind != RELKIND_TOASTVALUE))
 		{
 			scores->anl = (double) anltuples / Max(anlthresh, 1);
 			scores->anl *= autovacuum_analyze_score_weight;
@@ -3337,19 +3349,22 @@ relation_needs_vacanalyze(Oid relid,
 			*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+		if (!force_scores)
+		{
+			if (vac_ins_base_thresh >= 0)
+				elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 instuples, vacinsthresh, scores->vac_ins,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+			else
+				elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+		}
 	}
 }
 
-- 
2.47.3



  [application/octet-stream] v3-0003-Add-pg_stat_autovacuum_priority-view.patch (18.6K, 4-v3-0003-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From 791b8580a78afca44c97fa9722503bbbfde53728 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v3 3/3] Add pg_stat_autovacuum_priority view

d7965d65f introduced autovacuum prioritization with
scoring. This change adds a view to expose those scores.

The view shows a row per relation indicating whether it
needs vacuum or analyze, the score of each component
and the Max score across all components. This provides
a way to introspect autovacuum priority and provide
feedback on tuning of the related GUCs.

The underlying function pg_stat_get_autovacuum_priority()
scans all relations in the current database and computes
scores using relation_needs_vacanalyze(). By default,
only superusers and roles with privileges of
pg_read_all_stats can execute the function, as
controlled by the function's ACL in pg_proc.

The view also emits the relid, namespace and relname,
so it can be joined with other views like
pg_stat_all_tables and pg_stat_progress_vacuum for
complementary vacuum details.

Tests added to vacuum.sql

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 166 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  21 ++++
 src/backend/postmaster/autovacuum.c  |  84 ++++++++++++++
 src/include/catalog/pg_proc.dat      |  10 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  34 ++++++
 src/test/regress/sql/vacuum.sql      |  25 ++++
 8 files changed, 361 insertions(+)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..8ace8d4a9b0 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -5256,6 +5265,163 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority. The
+   <structfield>score</structfield>, <structfield>xid_score</structfield>,
+   and <structfield>mxid_score</structfield> values may be very large for
+   tables approaching wraparound, as these scores are scaled aggressively
+   once they surpass the failsafe age thresholds.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if autovacuum considers this table in need of vacuuming
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if autovacuum considers this table in need of analyzing
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>xid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>mxid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_dead_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_ins_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_stat_autovacuum_priority</structname> view can
+   be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+
+ </sect2>
+
  <sect2 id="monitoring-stats-functions">
   <title>Statistics Functions</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..55e08e20d80 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,27 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.xid_score,
+            S.mxid_score,
+            S.vacuum_dead_score,
+            S.vacuum_ins_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
+REVOKE ALL ON pg_stat_autovacuum_priority FROM PUBLIC;
+GRANT SELECT ON pg_stat_autovacuum_priority TO pg_read_all_stats;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index fbe670d375f..de3005c96e1 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -3676,3 +3678,85 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *		Returns the autovacuum priority score for all eligible relations
+ *		in the current database.
+ *
+ *		This follows the same setup as do_autovacuum(). Global state such
+ *		as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ *		computed here, while compute_autovac_score() handles the per-relation
+ *		score computation.
+ */
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+#define NUM_AV_SCORE_COLS 10
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+	TableScanDesc relScan;
+	HeapTuple	classTup;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		AutoVacOpts *avopts;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		avopts = extract_autovac_opts(classTup, pg_class_desc);
+
+		compute_autovac_score(classTup, pg_class_desc,
+							  effective_multixact_freeze_max_age, avopts,
+							  true, &dovacuum, &doanalyze,
+							  &wraparound, &scores);
+
+		if (avopts)
+			pfree(avopts);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(dovacuum);
+		values[2] = BoolGetDatum(doanalyze);
+		values[3] = BoolGetDatum(wraparound);
+		values[4] = Float8GetDatum(scores.max);
+		values[5] = Float8GetDatum(scores.xid);
+		values[6] = Float8GetDatum(scores.mxid);
+		values[7] = Float8GetDatum(scores.vac);
+		values[8] = Float8GetDatum(scores.vac_ins);
+		values[9] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..6268f8c6018 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,16 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,xid_score,mxid_score,vacuum_dead_score,vacuum_ins_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority',
+  proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..d2d3c3dd048 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.xid_score,
+    s.mxid_score,
+    s.vacuum_dead_score,
+    s.vacuum_ins_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, xid_score, mxid_score, vacuum_dead_score, vacuum_ins_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..bbee7457796 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,37 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Only xid_score and mxid_score
+-- are not tested, as they need to consume enough XID's. The test is
+-- run with autovacuum disabled for test stability as well as ensuring
+-- that priority fields are still populated in this case. The test ensures
+-- that the computed score is within an expected range.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_dead_score > 0 AND vacuum_dead_score <= 2 AS vacuum_dead_score,
+  vacuum_ins_score > 0 AND vacuum_ins_score <= 2 AS vacuum_ins_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_dead_score | vacuum_ins_score | analyze_score 
+--------------+---------------+-------+-------------------+------------------+---------------
+ t            | t             | t     | t                 | t                | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..a3bb1cc1dc2 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,28 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Only xid_score and mxid_score
+-- are not tested, as they need to consume enough XID's. The test is
+-- run with autovacuum disabled for test stability as well as ensuring
+-- that priority fields are still populated in this case. The test ensures
+-- that the computed score is within an expected range.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_dead_score > 0 AND vacuum_dead_score <= 2 AS vacuum_dead_score,
+  vacuum_ins_score > 0 AND vacuum_ins_score <= 2 AS vacuum_ins_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-31 15:38               ` Nathan Bossart <[email protected]>
  2026-03-31 16:15                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-03-31 15:38 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Robert Treat <[email protected]>; Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

I didn't read any of the thread, but I looked at the patches.

0001:

+ * force_scores set to true forces the computation of a score. This is useful for
+ * tools that wish to inspect scores outside of the do_vacuum() path.

I'm of two minds about this new function parameter.  On one hand, I see the
utility of forcing score calculations even when autovacuum is disabled.  On
the other hand, when autovacuum is disabled, the scores are actually 0.0,
and it's probably a good idea to report exactly what autovacuum workers
see.  I also see that we're not forcing the computation of the (M)XID
scores.  Is that intentional?

I wonder if we can rework this function to always calculate the scores,
even if autovacuum is disabled or !force_vacuum.  This way, both paths are
doing the exact same thing and reporting the same scores.  We might still
want an extra parameter to avoid DEBUG3 in the system view path, but that
seems like a reasonable difference between the two.

0002:

Seems okay to me.

0003:

+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_dead_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>

I think we should make sure the column names align with the names given to
the new parameters [0] and the new "Autovacuum Prioritization" section in
the docs [1].

[0] https://www.postgresql.org/docs/devel/runtime-config-vacuum.html#GUC-AUTOVACUUM-VACUUM-SCORE-WEIGHT
[1] https://www.postgresql.org/docs/devel/routine-vacuuming.html#AUTOVACUUM-PRIORITY

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 15:38               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-03-31 16:15                 ` Sami Imseih <[email protected]>
  2026-03-31 16:28                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-03-31 16:15 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Robert Treat <[email protected]>; Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

> + * force_scores set to true forces the computation of a score. This is useful for
> + * tools that wish to inspect scores outside of the do_vacuum() path.
>
> I'm of two minds about this new function parameter.  On one hand, I see the
> utility of forcing score calculations even when autovacuum is disabled.  On
> the other hand, when autovacuum is disabled, the scores are actually 0.0,
> and it's probably a good idea to report exactly what autovacuum workers
> see.

I went back and forth on this. Showing 0.0 when autovacuum is disabled
would reflect what autovacuum workers actually see, but I think the more
useful behavior is to always compute the score based on the table's actual
state. This way, a DBA who has disabled autovacuum on a table can still
see that its score is climbing and needs attention. The view shows need,
not eligibility. This will also make the view more useful for maintenance
jobs that wish to supplement autovacuum by looking at high scores
and triggering a manual vacuum for those tables.

> I also see that we're not forcing the computation of the (M)XID
> scores.  Is that intentional?

hmm, the force_score does not need to be in the force_vacuum path
because the score is calculated there naturally when the table is in
need of force_vacuum. The force_score is there to ensure that
we are not existing early in the autovacuum disabled case.

> I wonder if we can rework this function to always calculate the scores,
> even if autovacuum is disabled or !force_vacuum.  This way, both paths are
> doing the exact same thing and reporting the same scores.

I prefer that we still calculate the score as if autovacuum is enabled
for the reason above. I do think one potential middle ground is to have
needs_analyze, needs_vacuum, eligible_analyze, eligible_vacuum
fields to differentiate. I just rather not hide a score because a/v
is disabled on a table.

> +     <row>
> +      <entry role="catalog_table_entry"><para role="column_definition">
> +       <structfield>vacuum_dead_score</structfield> <type>double precision</type>
> +      </para>
> +      <para>
> +       Score component based on the estimated number of dead tuples
> +       needing removal by vacuum.
> +      </para></entry>
> +     </row>
>
> I think we should make sure the column names align with the names given to
> the new parameters [0] and the new "Autovacuum Prioritization" section in
> the docs [1].

I will look into this in the next rev.

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 15:38               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-03-31 16:15                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-31 16:28                   ` Nathan Bossart <[email protected]>
  2026-03-31 16:41                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-03-31 16:28 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Robert Treat <[email protected]>; Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

On Tue, Mar 31, 2026 at 11:15:35AM -0500, Sami Imseih wrote:
>> + * force_scores set to true forces the computation of a score. This is useful for
>> + * tools that wish to inspect scores outside of the do_vacuum() path.
>>
>> I'm of two minds about this new function parameter.  On one hand, I see the
>> utility of forcing score calculations even when autovacuum is disabled.  On
>> the other hand, when autovacuum is disabled, the scores are actually 0.0,
>> and it's probably a good idea to report exactly what autovacuum workers
>> see.
> 
> I went back and forth on this. Showing 0.0 when autovacuum is disabled
> would reflect what autovacuum workers actually see, but I think the more
> useful behavior is to always compute the score based on the table's actual
> state. This way, a DBA who has disabled autovacuum on a table can still
> see that its score is climbing and needs attention. The view shows need,
> not eligibility. This will also make the view more useful for maintenance
> jobs that wish to supplement autovacuum by looking at high scores
> and triggering a manual vacuum for those tables.

That's a fair point.

>> I also see that we're not forcing the computation of the (M)XID
>> scores.  Is that intentional?
> 
> hmm, the force_score does not need to be in the force_vacuum path
> because the score is calculated there naturally when the table is in
> need of force_vacuum. The force_score is there to ensure that
> we are not existing early in the autovacuum disabled case.

So, unless the table is beyond a freeze-max-age parameter, the (M)XID
scores will always be 0.0?

>> I wonder if we can rework this function to always calculate the scores,
>> even if autovacuum is disabled or !force_vacuum.  This way, both paths are
>> doing the exact same thing and reporting the same scores.
> 
> I prefer that we still calculate the score as if autovacuum is enabled
> for the reason above. I do think one potential middle ground is to have
> needs_analyze, needs_vacuum, eligible_analyze, eligible_vacuum
> fields to differentiate. I just rather not hide a score because a/v
> is disabled on a table.

My point is that instead of introducing a parameter to force score
computations, we could just _always_ do that in this function.  IOW maybe
we could use this as an opportunity to simplify the function while also
preparing it for the system view.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 15:38               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-03-31 16:15                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 16:28                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-03-31 16:41                     ` Sami Imseih <[email protected]>
  0 siblings, 0 replies; 60+ messages in thread

From: Sami Imseih @ 2026-03-31 16:41 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Robert Treat <[email protected]>; Bharath Rupireddy <[email protected]>; [email protected]; pgsql-hackers

> >> I also see that we're not forcing the computation of the (M)XID
> >> scores.  Is that intentional?
> >
> > hmm, the force_score does not need to be in the force_vacuum path
> > because the score is calculated there naturally when the table is in
> > need of force_vacuum. The force_score is there to ensure that
> > we are not existing early in the autovacuum disabled case.
>
> So, unless the table is beyond a freeze-max-age parameter, the (M)XID
> scores will always be 0.0?

You're right after thinking about this again. There is no reason why we
should treat the force_vacuum case differently. The score should still
be included so someone monitoring can see the xid|mxid_age climbing
well before it becomes an issue.

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-31 18:09               ` Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Bharath Rupireddy @ 2026-03-31 18:09 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Robert Treat <[email protected]>; [email protected]; pgsql-hackers

Hi,

On Mon, Mar 30, 2026 at 11:16 AM Sami Imseih <[email protected]> wrote:
>
> > I think we are also in agreement here, although based on my
> > experience, filtering out things like system and toast tables will be
> > common, but I don't see that changing what you said above.  On a
> > similar note, +1 to your changes in v2.
>
> Thanks!
>
> v3 now includes the refactoring [1] suggestion brought up by Alvarro

Thank you for sending the latest patches. Here are some comments:

1/ + while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)

Missing check_for_interrupts call while scanning the pg_class system catalog.

2/
+ avopts = extract_autovac_opts(classTup, pg_class_desc);
+
+ compute_autovac_score(classTup, pg_class_desc,
+   effective_multixact_freeze_max_age, avopts,
+   true, &dovacuum, &doanalyze,
+   &wraparound, &scores);
+
+ if (avopts)
+ pfree(avopts);
+

When a database has a large number of tables (which is quite common in
production scenarios), I expect the costs of palloc and pfree being
used for fetching autovacuum relopts would make this function slower.
Can we invent a new function or pass a caller-allocated AutoVacOpts
memory to just copy the relopts and use that in this tight loop when
scanning for all the relations?

3/
+ values[8] = Float8GetDatum(scores.vac_ins);
+ values[9] = Float8GetDatum(scores.anl);

Nit: It's a matter of taste. How about using something like below
instead of hardcoded column numbers? I expect this view to grow in the
future, so it helps to keep things simple.

values[i++] = Float8GetDatum(scores.anl);
Assert(i == NUM_AV_SCORE_COLS);

4/
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.

How about adding "as of the moment" to convey that it doesn't report
what currently running autovacuum or pending autovacuum would
consider?

5/ Also, can we add a simple paragraph on how to interpret and take
actions based on the scores reported (like prioritizing one table over
the other - adjust these parameters in the table's relopts or
something like that - no need to cover all the possible cases, but
just one example would be sufficient for the user to understand)?

6/ +  descr => 'statistics: autovacuum priority scores for all relations',

s/"for all relations"/"for all relations in the current database"

7/ Addition of force_scores to relation_needs_vacanalyze makes the
code unreadable (IMO) with a lot of if-else branching. Why not make
force_vacuum an option and pass it as true from the stats function and
leave a note in the function comment on when to use this parameter?
Would something like that work? Also, when autovacuum is disabled
(either via GUC or via relopts), we don't want to calculate and report
any scores. IMHO, this keeps things simple and code readable.

--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
@ 2026-03-31 18:34                 ` Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-03-31 18:34 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; +Cc: Sami Imseih <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

On Tue, Mar 31, 2026 at 11:09:43AM -0700, Bharath Rupireddy wrote:
> 1/ + while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
> 
> Missing check_for_interrupts call while scanning the pg_class system catalog.



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-01 02:46                   ` Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-01 02:46 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> > 1/ + while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
> >
> > Missing check_for_interrupts call while scanning the pg_class system catalog.
>
> From a glance I don't see one in the scanning code in do_autovacuum(),
> either.  I'm not sure we need to be worried about this.

Yes, I will leave it out. Even for catalogs with thousands of tables, I don't
foresee this being an issue.

> > + values[8] = Float8GetDatum(scores.vac_ins);
> > + values[9] = Float8GetDatum(scores.anl);
> >
> > Nit: It's a matter of taste. How about using something like below
> > instead of hardcoded column numbers? I expect this view to grow in the
> > future, so it helps to keep things simple.
> >
> > values[i++] = Float8GetDatum(scores.anl);
> > Assert(i == NUM_AV_SCORE_COLS);
>
> I don't think either way is substantially better.

I agree. using numbers is more readable IMO.

> > +     <row>
> > +      <entry role="catalog_table_entry"><para role="column_definition">
> > +       <structfield>vacuum_dead_score</structfield> <type>double precision</type>
> > +      </para>
> > +      <para>
> > +       Score component based on the estimated number of dead tuples
> > +       needing removal by vacuum.
> > +      </para></entry>
> > +     </row>
> >
> > I think we should make sure the column names align with the names given to
> > the new parameters [0] and the new "Autovacuum Prioritization" section in
> > the docs [1].

> I will look into this in the next rev.

The field names now match the GUC names (without the _weight). We might as well
also make this name change in the AutoVacuumScore struct.

I attached v4 which includes 4 patches in the set to address earlier comments:

0001:

This modifies relation_needs_vacanalyze() to always compute the score so
the monitoring view can report the score regardless if autovacuum is enabled
on the table. AutoVacuumScores is renamed to AutoVacuumPriority as it
now also tracks needs_analyze, needs_vacuum and is_wraparound which
are different from the dovacuum, doanalyze and wraparound output parameters
which are acted on by autovacuum. This is a clean separation of reporting
the scores/needs for autovacuum/analyze and the eligibility for autovacuum
which is based on the state of autovacuum being enabled and set on the table
level.

0002:

relation_needs_vacanalyze() takes a new elevel argument to control logging.
Callers pass DEBUG3 for autovacuum or 0 to suppress logging.

0003:

Alvaro's earlier suggestion to factor out recheck_relation_needs_vacanalyze()
and introduce compute_autovac_score()

0004:

The view implementation with field names that better match the GUCs as suggested
by Nathan in an earlier comment.

--
Sami


Attachments:

  [application/octet-stream] v4-0001-Always-compute-autovacuum-priority-scores.patch (17.1K, 2-v4-0001-Always-compute-autovacuum-priority-scores.patch)
  download | inline diff:
From f12fd986764fc218cae670ddc4f6ba6ec1fdcc98 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:25:30 +0000
Subject: [PATCH v4 1/4] Always compute autovacuum priority scores

Previously, XID/MXID age scores were only computed when
a table was at wraparound risk, and threshold-based
scores were only computed when autovacuum was globally
active. This meant the scores were unavailable for
tables not yet at risk or with autovacuum disabled.

A future patch to monitor the scores and the need
for vacuum/analyze must be able to compute these
values regardless of autovacuum state on the table.
Therefore, separate the need for vacuum and analyze
and track them in AutoVacuumScore so they can be
used for this purpose rather than the output
dovacuum, doanalyze and wraparound parameters that
are acted upon by autovacuum.

AutoVacuumScores is also renamed to AutoVacuumPriority
as it no longer just tracks scores.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 216 ++++++++++++++--------------
 src/tools/pgindent/typedefs.list    |   2 +-
 2 files changed, 113 insertions(+), 105 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6694f485216..2ca15aee4eb 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -318,8 +318,9 @@ static MemoryContext DatabaseListCxt = NULL;
 
 /*
  * This struct is used by relation_needs_vacanalyze() to return the table's
- * score (i.e., the maximum of the component scores) as well as the component
- * scores themselves.
+ * autovacuum priority, including the overall score (i.e., the maximum of the
+ * component scores), the component scores themselves, and whether the table
+ * needs vacuum, analyze, or is at risk of wraparound.
  */
 typedef struct
 {
@@ -329,7 +330,10 @@ typedef struct
 	double		vac;			/* vacuum component */
 	double		vac_ins;		/* vacuum insert component */
 	double		anl;			/* analyze component */
-} AutoVacuumScores;
+	bool		needs_vacuum;	/* threshold exceeded for vacuum */
+	bool		needs_analyze;	/* threshold exceeded for analyze */
+	bool		is_wraparound;	/* at risk of XID/MXID wraparound */
+} AutoVacuumPriority;
 
 /*
  * This struct is used to track and sort the list of tables to process.
@@ -379,7 +383,7 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
-									  AutoVacuumScores *scores);
+									  AutoVacuumPriority *priority);
 
 static void autovacuum_do_vac_analyze(autovac_table *tab,
 									  BufferAccessStrategy bstrategy);
@@ -2034,7 +2038,7 @@ do_autovacuum(void)
 		bool		dovacuum;
 		bool		doanalyze;
 		bool		wraparound;
-		AutoVacuumScores scores;
+		AutoVacuumPriority priority;
 
 		if (classForm->relkind != RELKIND_RELATION &&
 			classForm->relkind != RELKIND_MATVIEW)
@@ -2076,7 +2080,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &priority);
 
 		/* Relations that need work are added to tables_to_process */
 		if (dovacuum || doanalyze)
@@ -2084,7 +2088,7 @@ do_autovacuum(void)
 			TableToProcess *table = palloc_object(TableToProcess);
 
 			table->oid = relid;
-			table->score = scores.max;
+			table->score = priority.max;
 			tables_to_process = lappend(tables_to_process, table);
 		}
 
@@ -2142,7 +2146,7 @@ do_autovacuum(void)
 		bool		dovacuum;
 		bool		doanalyze;
 		bool		wraparound;
-		AutoVacuumScores scores;
+		AutoVacuumPriority priority;
 
 		/*
 		 * We cannot safely process other backends' temp tables, so skip 'em.
@@ -2176,7 +2180,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &priority);
 
 		/* ignore analyze for toast tables */
 		if (dovacuum)
@@ -2184,7 +2188,7 @@ do_autovacuum(void)
 			TableToProcess *table = palloc_object(TableToProcess);
 
 			table->oid = relid;
-			table->score = scores.max;
+			table->score = priority.max;
 			tables_to_process = lappend(tables_to_process, table);
 		}
 
@@ -2985,7 +2989,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 								  bool *wraparound)
 {
 	PgStat_StatTabEntry *tabentry;
-	AutoVacuumScores scores;
+	AutoVacuumPriority priority;
 
 	/* fetch the pgstat table entry */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
@@ -2994,7 +2998,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores);
+							  &priority);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
@@ -3030,10 +3034,10 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * transactions back, and if its relminmxid is more than
  * multixact_freeze_max_age multixacts back.
  *
- * A table whose autovacuum_enabled option is false is
- * automatically skipped (unless we have to vacuum it due to freeze_max_age).
- * Thus autovacuum can be disabled for specific tables. Also, when the cumulative
- * stats system does not have data about a table, it will be skipped.
+ * A table whose autovacuum_enabled option is false is automatically skipped
+ * by autovacuum (unless we have to vacuum it due to freeze_max_age),
+ * but scores are still computed.  Also, when the cumulative stats system does
+ * not have data about a table, threshold-based scores will be zero.
  *
  * A table whose vac_base_thresh value is < 0 takes the base value from the
  * autovacuum_vacuum_threshold GUC variable.  Similarly, a vac_scale_factor
@@ -3061,10 +3065,10 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * compared to the vacuum threshold, and the number of inserted/updated/deleted
  * tuples compared to the analyze threshold.
  *
- * One exception to the previous paragraph is for tables nearing wraparound,
- * i.e., those that have surpassed the effective failsafe ages.  In that case,
- * the relfrozen/relminmxid-based score is scaled aggressively so that the
- * table has a decent chance of sorting to the front of the list.
+ * Furthermore, for tables nearing wraparound, i.e., those that have surpassed
+ * the effective failsafe ages, the relfrozen/relminmxid-based score is scaled
+ * aggressively so that the table has a decent chance of sorting to the front
+ * of the list.
  *
  * To adjust how strongly each component contributes to the score, the
  * following parameters can be adjusted from their default of 1.0 to anywhere
@@ -3077,9 +3081,12 @@ recheck_relation_needs_vacanalyze(Oid relid,
  *     autovacuum_vacuum_insert_score_weight
  *     autovacuum_analyze_score_weight
  *
- * The autovacuum table score is returned in scores->max.  The component scores
- * are also returned in the "scores" argument via the other members of the
- * AutoVacuumScores struct.
+ * The autovacuum table score is returned in priority->max.  The component scores
+ * are also returned in the "priority" argument via the other members of the
+ * AutoVacuumPriority struct.
+ *
+ * Priority scores are always computed.  dovacuum and doanalyze are only set when
+ * autovacuum is active and enabled for the relation.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3091,7 +3098,7 @@ relation_needs_vacanalyze(Oid relid,
 						  bool *dovacuum,
 						  bool *doanalyze,
 						  bool *wraparound,
-						  AutoVacuumScores *scores)
+						  AutoVacuumPriority *priority)
 {
 	bool		force_vacuum;
 	bool		av_enabled;
@@ -3122,11 +3129,15 @@ relation_needs_vacanalyze(Oid relid,
 	TransactionId relfrozenxid;
 	MultiXactId relminmxid;
 	MultiXactId multiForceLimit;
+	uint32		xid_age;
+	uint32		mxid_age;
+	int			effective_xid_failsafe_age;
+	int			effective_mxid_failsafe_age;
 
 	Assert(classForm != NULL);
 	Assert(OidIsValid(relid));
 
-	memset(scores, 0, sizeof(AutoVacuumScores));
+	memset(priority, 0, sizeof(AutoVacuumPriority));
 	*dovacuum = false;
 	*doanalyze = false;
 
@@ -3196,74 +3207,64 @@ relation_needs_vacanalyze(Oid relid,
 	}
 	*wraparound = force_vacuum;
 
-	/* Update the score. */
-	if (force_vacuum)
-	{
-		uint32		xid_age;
-		uint32		mxid_age;
-		int			effective_xid_failsafe_age;
-		int			effective_mxid_failsafe_age;
+	/*
+	 * To calculate the (M)XID age portion of the score, divide the age by its
+	 * respective *_freeze_max_age parameter.
+	 */
+	xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
+	mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
 
-		/*
-		 * To calculate the (M)XID age portion of the score, divide the age by
-		 * its respective *_freeze_max_age parameter.
-		 */
-		xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
-		mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
+	priority->xid = (double) xid_age / freeze_max_age;
+	priority->mxid = (double) mxid_age / multixact_freeze_max_age;
 
-		scores->xid = (double) xid_age / freeze_max_age;
-		scores->mxid = (double) mxid_age / multixact_freeze_max_age;
+	/*
+	 * To ensure tables are given increased priority once they begin
+	 * approaching wraparound, we scale the score aggressively if the ages
+	 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
+	 *
+	 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
+	 * less than 105% the value of the respective *_freeze_max_age parameter.
+	 * Note that per-table settings could result in a low score even if the
+	 * table surpasses the failsafe settings.  However, this is a strange
+	 * enough corner case that we don't bother trying to handle it.
+	 *
+	 * We further adjust the effective failsafe ages with the weight
+	 * parameters so that increasing them lowers the ages at which we begin
+	 * scaling aggressively.
+	 */
+	effective_xid_failsafe_age = Max(vacuum_failsafe_age,
+									 autovacuum_freeze_max_age * 1.05);
+	effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
+									  autovacuum_multixact_freeze_max_age * 1.05);
 
-		/*
-		 * To ensure tables are given increased priority once they begin
-		 * approaching wraparound, we scale the score aggressively if the ages
-		 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
-		 *
-		 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
-		 * less than 105% the value of the respective *_freeze_max_age
-		 * parameter.  Note that per-table settings could result in a low
-		 * score even if the table surpasses the failsafe settings.  However,
-		 * this is a strange enough corner case that we don't bother trying to
-		 * handle it.
-		 *
-		 * We further adjust the effective failsafe ages with the weight
-		 * parameters so that increasing them lowers the ages at which we
-		 * begin scaling aggressively.
-		 */
-		effective_xid_failsafe_age = Max(vacuum_failsafe_age,
-										 autovacuum_freeze_max_age * 1.05);
-		effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
-										  autovacuum_multixact_freeze_max_age * 1.05);
+	if (autovacuum_freeze_score_weight > 1.0)
+		effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
+	if (autovacuum_multixact_freeze_score_weight > 1.0)
+		effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
 
-		if (autovacuum_freeze_score_weight > 1.0)
-			effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
-		if (autovacuum_multixact_freeze_score_weight > 1.0)
-			effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
+	if (xid_age >= effective_xid_failsafe_age)
+		priority->xid = pow(priority->xid, Max(1.0, (double) xid_age / 100000000));
+	if (mxid_age >= effective_mxid_failsafe_age)
+		priority->mxid = pow(priority->mxid, Max(1.0, (double) mxid_age / 100000000));
 
-		if (xid_age >= effective_xid_failsafe_age)
-			scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
-		if (mxid_age >= effective_mxid_failsafe_age)
-			scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
+	priority->xid *= autovacuum_freeze_score_weight;
+	priority->mxid *= autovacuum_multixact_freeze_score_weight;
 
-		scores->xid *= autovacuum_freeze_score_weight;
-		scores->mxid *= autovacuum_multixact_freeze_score_weight;
+	priority->max = Max(priority->xid, priority->mxid);
 
-		scores->max = Max(scores->xid, scores->mxid);
+	if (force_vacuum)
+	{
 		*dovacuum = true;
+		priority->is_wraparound = priority->needs_vacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
-		return;
-
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, make a threshold-based decision
+	 * whether to vacuum and/or analyze, and compute the corresponding
+	 * priority. dovacuum/doanalyze are only set when autovacuum is active and
+	 * enabled for this table.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry)
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,52 +3305,59 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Determine if this table needs vacuum, and if it does, update the
+		 * score and mark the table as needing vacuum.
 		 */
 		if (vactuples > vacthresh)
 		{
-			scores->vac = (double) vactuples / Max(vacthresh, 1);
-			scores->vac *= autovacuum_vacuum_score_weight;
-			scores->max = Max(scores->max, scores->vac);
-			*dovacuum = true;
+			priority->vac = (double) vactuples / Max(vacthresh, 1);
+			priority->vac *= autovacuum_vacuum_score_weight;
+			priority->max = Max(priority->max, priority->vac);
+			priority->needs_vacuum = true;
+			if (av_enabled && AutoVacuumingActive())
+				*dovacuum = true;
 		}
 
 		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
 		{
-			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
-			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
-			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
+			priority->vac_ins = (double) instuples / Max(vacinsthresh, 1);
+			priority->vac_ins *= autovacuum_vacuum_insert_score_weight;
+			priority->max = Max(priority->max, priority->vac_ins);
+			priority->needs_vacuum = true;
+			if (av_enabled && AutoVacuumingActive())
+				*dovacuum = true;
 		}
 
 		/*
-		 * Determine if this table needs analyze, and update the score if it
-		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
+		 * Determine if this table needs analyze, and if it does, update the
+		 * score and mark the table as needing analyze. Note that we don't
+		 * analyze TOAST tables and pg_statistic.
 		 */
 		if (anltuples > anlthresh &&
 			relid != StatisticRelationId &&
 			classForm->relkind != RELKIND_TOASTVALUE)
 		{
-			scores->anl = (double) anltuples / Max(anlthresh, 1);
-			scores->anl *= autovacuum_analyze_score_weight;
-			scores->max = Max(scores->max, scores->anl);
-			*doanalyze = true;
+			priority->anl = (double) anltuples / Max(anlthresh, 1);
+			priority->anl *= autovacuum_analyze_score_weight;
+			priority->max = Max(priority->max, priority->anl);
+			priority->needs_analyze = true;
+			if (av_enabled && AutoVacuumingActive())
+				*doanalyze = true;
 		}
 
 		if (vac_ins_base_thresh >= 0)
 			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
 				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+				 vactuples, vacthresh, priority->vac,
+				 instuples, vacinsthresh, priority->vac_ins,
+				 anltuples, anlthresh, priority->anl,
+				 priority->xid, priority->mxid);
 		else
 			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
 				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+				 vactuples, vacthresh, priority->vac,
+				 anltuples, anlthresh, priority->anl,
+				 priority->xid, priority->mxid);
 	}
 }
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8e9c06547d6..de874c62cd3 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -187,7 +187,7 @@ AuthToken
 AutoPrewarmReadStreamData
 AutoPrewarmSharedState
 AutoVacOpts
-AutoVacuumScores
+AutoVacuumPriority
 AutoVacuumShmemStruct
 AutoVacuumWorkItem
 AutoVacuumWorkItemType
-- 
2.47.3



  [application/octet-stream] v4-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch (4.1K, 3-v4-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch)
  download | inline diff:
From 2e49700cc2ce24399a5f260c78ba907c519dffe7 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:33:18 +0000
Subject: [PATCH v4 2/4] Add elevel parameter to relation_needs_vacanalyze

Allow callers to control the log level for debug output
by passing an elevel parameter. Passing 0 suppresses
logging. This prepares the function for use by a future
view that should not emit debug messages when queried.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 2ca15aee4eb..72ad30f6ae7 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -383,7 +383,8 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
-									  AutoVacuumPriority *priority);
+									  AutoVacuumPriority *priority,
+									  int elevel);
 
 static void autovacuum_do_vac_analyze(autovac_table *tab,
 									  BufferAccessStrategy bstrategy);
@@ -2080,7 +2081,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &priority);
+								  &priority, DEBUG3);
 
 		/* Relations that need work are added to tables_to_process */
 		if (dovacuum || doanalyze)
@@ -2180,7 +2181,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &priority);
+								  &priority, DEBUG3);
 
 		/* ignore analyze for toast tables */
 		if (dovacuum)
@@ -2998,7 +2999,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &priority);
+							  &priority, DEBUG3);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
@@ -3087,6 +3088,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
  *
  * Priority scores are always computed.  dovacuum and doanalyze are only set when
  * autovacuum is active and enabled for the relation.
+ *
+ * elevel controls the log level for debug output.  Pass 0 to suppress logging.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3098,7 +3101,8 @@ relation_needs_vacanalyze(Oid relid,
 						  bool *dovacuum,
 						  bool *doanalyze,
 						  bool *wraparound,
-						  AutoVacuumPriority *priority)
+						  AutoVacuumPriority *priority,
+						  int elevel)
 {
 	bool		force_vacuum;
 	bool		av_enabled;
@@ -3345,15 +3349,15 @@ relation_needs_vacanalyze(Oid relid,
 				*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+		if (elevel > 0 && vac_ins_base_thresh >= 0)
+			elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
 				 NameStr(classForm->relname),
 				 vactuples, vacthresh, priority->vac,
 				 instuples, vacinsthresh, priority->vac_ins,
 				 anltuples, anlthresh, priority->anl,
 				 priority->xid, priority->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+		else if (elevel > 0)
+			elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
 				 NameStr(classForm->relname),
 				 vactuples, vacthresh, priority->vac,
 				 anltuples, anlthresh, priority->anl,
-- 
2.47.3



  [application/octet-stream] v4-0004-Add-pg_stat_autovacuum_priority-view.patch (18.5K, 4-v4-0004-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From f53d03702f4882a9135176a3932a93dd668cfca1 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v4 4/4] Add pg_stat_autovacuum_priority view

d7965d65f introduced autovacuum prioritization with
scoring. This change adds a view to expose those scores.

The view shows a row per relation indicating whether it
needs vacuum or analyze, the score of each component
and the Max score across all components. This provides
a way to introspect autovacuum priority and provide
feedback on tuning of the related GUCs.

The underlying function pg_stat_get_autovacuum_priority()
scans all relations in the current database and computes
scores using compute_autovac_score(). By default,
only superusers and roles with privileges of
pg_read_all_stats can execute the function, as
controlled by the function's ACL in pg_proc.

The view also emits the relid, namespace and relname,
so it can be joined with other views like
pg_stat_all_tables and pg_stat_progress_vacuum for
complementary vacuum details.

Tests added to vacuum.sql

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 166 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  21 ++++
 src/backend/postmaster/autovacuum.c  |  86 ++++++++++++++
 src/include/catalog/pg_proc.dat      |  10 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  32 ++++++
 src/test/regress/sql/vacuum.sql      |  23 ++++
 8 files changed, 359 insertions(+)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..791850bafe7 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -5256,6 +5265,163 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority. The
+   <structfield>score</structfield>, <structfield>freeze_score</structfield>,
+   and <structfield>multixact_freeze_score</structfield> values may be very large for
+   tables approaching wraparound, as these scores are scaled aggressively
+   once they surpass the failsafe age thresholds.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the vacuum threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the analyze threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>multixact_freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_insert_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_stat_autovacuum_priority</structname> view can
+   be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+
+ </sect2>
+
  <sect2 id="monitoring-stats-functions">
   <title>Statistics Functions</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..30bbd55e330 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,27 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.freeze_score,
+            S.multixact_freeze_score,
+            S.vacuum_score,
+            S.vacuum_insert_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
+REVOKE ALL ON pg_stat_autovacuum_priority FROM PUBLIC;
+GRANT SELECT ON pg_stat_autovacuum_priority TO pg_read_all_stats;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 4b0d2526f5b..a469d2858ff 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -3675,3 +3677,87 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *
+ * Returns the autovacuum priority score for a relation as well as if the
+ * relation needs vacuum or analyze, and if the vacuum is a force vacuum
+ * due to wraparound.
+ *
+ * This follows the same setup as do_autovacuum(). Global state such
+ * as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ * computed here, while compute_autovac_score() handles the per-relation
+ * score computation.
+ */
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+#define NUM_AV_SCORE_COLS 10
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+	TableScanDesc relScan;
+	HeapTuple	classTup;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumPriority priority;
+		AutoVacOpts *avopts;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		avopts = extract_autovac_opts(classTup, pg_class_desc);
+
+		compute_autovac_score(classTup, pg_class_desc,
+							  effective_multixact_freeze_max_age, avopts,
+							  0, &dovacuum, &doanalyze,
+							  &wraparound, &priority);
+
+		if (avopts)
+			pfree(avopts);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(priority.needs_vacuum);
+		values[2] = BoolGetDatum(priority.needs_analyze);
+		values[3] = BoolGetDatum(priority.is_wraparound);
+		values[4] = Float8GetDatum(priority.max);
+		values[5] = Float8GetDatum(priority.xid);
+		values[6] = Float8GetDatum(priority.mxid);
+		values[7] = Float8GetDatum(priority.vac);
+		values[8] = Float8GetDatum(priority.vac_ins);
+		values[9] = Float8GetDatum(priority.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..6f5d199a506 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,16 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,freeze_score,multixact_freeze_score,vacuum_score,vacuum_insert_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority',
+  proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..53bf9a46e4f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.freeze_score,
+    s.multixact_freeze_score,
+    s.vacuum_score,
+    s.vacuum_insert_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, freeze_score, multixact_freeze_score, vacuum_score, vacuum_insert_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..9cbc2f6a14b 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,35 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_score | vacuum_insert_score | analyze_score 
+--------------+---------------+-------+--------------+---------------------+---------------
+ t            | t             | t     | t            | t                   | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..556fe3127f4 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,26 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
-- 
2.47.3



  [application/octet-stream] v4-0003-Refactor-autovacuum-score-computation-into-comput.patch (4.3K, 5-v4-0003-Refactor-autovacuum-score-computation-into-comput.patch)
  download | inline diff:
From e36b8b1427757b1e09c32713b0b4912a0c806073 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:43:25 +0000
Subject: [PATCH v4 3/4] Refactor autovacuum score computation into
 compute_autovac_score

Autovacuum priority score will be computed for several
reasons: Autovacuum processing and a future monitoring
view to report the scores per table.

This consolidates this code into a new routine
compute_autovac_score() and also removes the need for
recheck_relation_needs_vacanalyze() which is doing the
same thing.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 47 ++++++++++++++---------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 72ad30f6ae7..4b0d2526f5b 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -374,10 +374,11 @@ static void FreeWorkerInfo(int code, Datum arg);
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 											TupleDesc pg_class_desc,
 											int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
-											  Form_pg_class classForm,
-											  int effective_multixact_freeze_max_age,
-											  bool *dovacuum, bool *doanalyze, bool *wraparound);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  AutoVacOpts *relopts, int elevel,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumPriority *priority);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
@@ -2836,6 +2837,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	bool		wraparound;
 	AutoVacOpts *avopts;
 	bool		free_avopts = false;
+	AutoVacuumPriority priority;
 
 	/* fetch the relation's relcache entry */
 	classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2861,9 +2863,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			avopts = &hentry->ar_reloptions;
 	}
 
-	recheck_relation_needs_vacanalyze(relid, avopts, classForm,
-									  effective_multixact_freeze_max_age,
-									  &dovacuum, &doanalyze, &wraparound);
+	compute_autovac_score(classTup, pg_class_desc,
+						  effective_multixact_freeze_max_age,
+						  avopts, DEBUG3,
+						  &dovacuum, &doanalyze, &wraparound, &priority);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -2973,33 +2976,29 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 }
 
 /*
- * recheck_relation_needs_vacanalyze
- *
- * Subroutine for table_recheck_autovac.
- *
- * Fetch the pgstat of a relation and recheck whether a relation
- * needs to be vacuumed or analyzed.
+ * compute_autovac_score
+ *		Fetch the pgstat entry for a relation and call
+ *		relation_needs_vacanalyze() to determine whether it needs
+ *		vacuum or analyze and compute its priority scores.
  */
 static void
-recheck_relation_needs_vacanalyze(Oid relid,
-								  AutoVacOpts *avopts,
-								  Form_pg_class classForm,
-								  int effective_multixact_freeze_max_age,
-								  bool *dovacuum,
-								  bool *doanalyze,
-								  bool *wraparound)
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  AutoVacOpts *relopts, int elevel,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumPriority *priority)
 {
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 	PgStat_StatTabEntry *tabentry;
-	AutoVacuumPriority priority;
 
 	/* fetch the pgstat table entry */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
+											  classForm->oid);
 
-	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &priority, DEBUG3);
+							  priority, elevel);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-01 15:07                     ` Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-01 15:07 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

0001:

-                                      AutoVacuumScores *scores);
+                                      AutoVacuumPriority *priority);

IMHO we need to minimize these kinds of extraneous changes in this patch
set.  AutoVacuumScores still seems accurate enough, even when the struct
contains some extra bool members.

- * A table whose autovacuum_enabled option is false is
- * automatically skipped (unless we have to vacuum it due to freeze_max_age).
- * Thus autovacuum can be disabled for specific tables. Also, when the cumulative
- * stats system does not have data about a table, it will be skipped.
+ * A table whose autovacuum_enabled option is false is automatically skipped
+ * by autovacuum (unless we have to vacuum it due to freeze_max_age),
+ * but scores are still computed.  Also, when the cumulative stats system does
+ * not have data about a table, threshold-based scores will be zero.

I don't think we need to update this comment.

- * One exception to the previous paragraph is for tables nearing wraparound,
- * i.e., those that have surpassed the effective failsafe ages.  In that case,
- * the relfrozen/relminmxid-based score is scaled aggressively so that the
- * table has a decent chance of sorting to the front of the list.
+ * Furthermore, for tables nearing wraparound, i.e., those that have surpassed
+ * the effective failsafe ages, the relfrozen/relminmxid-based score is scaled
+ * aggressively so that the table has a decent chance of sorting to the front
+ * of the list.

Or this one.

+ * Priority scores are always computed.  dovacuum and doanalyze are only set when
+ * autovacuum is active and enabled for the relation.

I think we should more explicitly state that while scores->needs_vacuum and
friends are always set regardless of whether autovacuum is enabled, the
return parameters dovacuum, etc., are not.  Or perhaps we should return
whether autovacuum is enabled in the struct and consolidate the return
parameters and the struct members.  WDYT?

0002:

Seems fine.

0003:

Seems fine.

0004:

+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;

What do you think about ordering by score so this view automatically shows
the tables most in need of vacuuming/analyzing first?

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-01 16:58                       ` Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-01 16:58 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> 0001:
>
> -                                      AutoVacuumScores *scores);
> +                                      AutoVacuumPriority *priority);
>
> IMHO we need to minimize these kinds of extraneous changes in this patch
> set.  AutoVacuumScores still seems accurate enough, even when the struct
> contains some extra bool members.
>
> - * A table whose autovacuum_enabled option is false is
> - * automatically skipped (unless we have to vacuum it due to freeze_max_age).
> - * Thus autovacuum can be disabled for specific tables. Also, when the cumulative
> - * stats system does not have data about a table, it will be skipped.
> + * A table whose autovacuum_enabled option is false is automatically skipped
> + * by autovacuum (unless we have to vacuum it due to freeze_max_age),
> + * but scores are still computed.  Also, when the cumulative stats system does
> + * not have data about a table, threshold-based scores will be zero.
>
> I don't think we need to update this comment.
>
> - * One exception to the previous paragraph is for tables nearing wraparound,
> - * i.e., those that have surpassed the effective failsafe ages.  In that case,
> - * the relfrozen/relminmxid-based score is scaled aggressively so that the
> - * table has a decent chance of sorting to the front of the list.
> + * Furthermore, for tables nearing wraparound, i.e., those that have surpassed
> + * the effective failsafe ages, the relfrozen/relminmxid-based score is scaled
> + * aggressively so that the table has a decent chance of sorting to the front
> + * of the list.
>
> Or this one.

Fixed both.

> + * Priority scores are always computed.  dovacuum and doanalyze are only set when
> + * autovacuum is active and enabled for the relation.
>
> I think we should more explicitly state that while scores->needs_vacuum and
> friends are always set regardless of whether autovacuum is enabled, the
> return parameters dovacuum, etc., are not.

I made this more explicit by saying " All fields in AutoVacuumScores
are always computed
regardless of autovacuum settings...." I think that is clear enough.

> Or perhaps we should return
> whether autovacuum is enabled in the struct and consolidate the return
> parameters and the struct members.  WDYT?

dovacuum, doanalyze will be unused by callers like the sql function, and putting
them in the struct could make this cleaner, but I don't think it's
worth it to blur
the purpose of the struct. I rather keep it just for score computation purposes.

> 0002:
>
> Seems fine.

I found a bug in my v4 that I fixed.

+        if (elevel > 0 && vac_ins_base_thresh >= 0)

is wrong. It should be:

if (elevel > 0)
{
      if (vac_ins_base_thresh >= 0)


> 0004:
>
> +    FROM pg_stat_get_autovacuum_priority() S
> +         JOIN pg_class C ON C.oid = S.relid
> +         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
>
> What do you think about ordering by score so this view automatically shows
> the tables most in need of vacuuming/analyzing first?

I thought about that initially, but we don't really have an example where the
data is ordered in a stats view ( or any other catalog view ), and I prefer not
to impose that on the user automatically.

See v5.

--
Sami


Attachments:

  [application/octet-stream] v5-0001-Always-compute-autovacuum-priority-scores.patch (10.0K, 2-v5-0001-Always-compute-autovacuum-priority-scores.patch)
  download | inline diff:
From c4f01cac83e2e265ea018c625bd4cb1b69634f79 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:25:30 +0000
Subject: [PATCH v5 1/4] Always compute autovacuum priority scores

Previously, XID/MXID age scores were only computed when
a table was at wraparound risk, and threshold-based
scores were only computed when autovacuum was globally
active. This meant the scores were unavailable for
tables not yet at risk or with autovacuum disabled.

A future patch to monitor the scores and the need
for vacuum/analyze must be able to compute these
values regardless of autovacuum state on the table.
Therefore, separate the need for vacuum and analyze
and track them in AutoVacuumScores so they can be
used for this purpose rather than the output
dovacuum, doanalyze and wraparound parameters that
are acted upon by autovacuum.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 139 +++++++++++++++-------------
 1 file changed, 74 insertions(+), 65 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6694f485216..013b49eb69c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -318,8 +318,9 @@ static MemoryContext DatabaseListCxt = NULL;
 
 /*
  * This struct is used by relation_needs_vacanalyze() to return the table's
- * score (i.e., the maximum of the component scores) as well as the component
- * scores themselves.
+ * autovacuum priority, including the overall score (i.e., the maximum of the
+ * component scores), the component scores themselves, and whether the table
+ * needs vacuum, analyze, or is at risk of wraparound.
  */
 typedef struct
 {
@@ -329,6 +330,9 @@ typedef struct
 	double		vac;			/* vacuum component */
 	double		vac_ins;		/* vacuum insert component */
 	double		anl;			/* analyze component */
+	bool		needs_vacuum;	/* threshold exceeded for vacuum */
+	bool		needs_analyze;	/* threshold exceeded for analyze */
+	bool		is_wraparound;	/* at risk of XID/MXID wraparound */
 } AutoVacuumScores;
 
 /*
@@ -3080,6 +3084,10 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * The autovacuum table score is returned in scores->max.  The component scores
  * are also returned in the "scores" argument via the other members of the
  * AutoVacuumScores struct.
+ *
+ * All fields in AutoVacuumScores are always computed regardless of autovacuum
+ * settings.  The dovacuum and doanalyze output parameters are only set when
+ * autovacuum is globally active and enabled for the relation.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3122,6 +3130,10 @@ relation_needs_vacanalyze(Oid relid,
 	TransactionId relfrozenxid;
 	MultiXactId relminmxid;
 	MultiXactId multiForceLimit;
+	uint32		xid_age;
+	uint32		mxid_age;
+	int			effective_xid_failsafe_age;
+	int			effective_mxid_failsafe_age;
 
 	Assert(classForm != NULL);
 	Assert(OidIsValid(relid));
@@ -3196,74 +3208,64 @@ relation_needs_vacanalyze(Oid relid,
 	}
 	*wraparound = force_vacuum;
 
-	/* Update the score. */
-	if (force_vacuum)
-	{
-		uint32		xid_age;
-		uint32		mxid_age;
-		int			effective_xid_failsafe_age;
-		int			effective_mxid_failsafe_age;
+	/*
+	 * To calculate the (M)XID age portion of the score, divide the age by its
+	 * respective *_freeze_max_age parameter.
+	 */
+	xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
+	mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
 
-		/*
-		 * To calculate the (M)XID age portion of the score, divide the age by
-		 * its respective *_freeze_max_age parameter.
-		 */
-		xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
-		mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
+	scores->xid = (double) xid_age / freeze_max_age;
+	scores->mxid = (double) mxid_age / multixact_freeze_max_age;
 
-		scores->xid = (double) xid_age / freeze_max_age;
-		scores->mxid = (double) mxid_age / multixact_freeze_max_age;
+	/*
+	 * To ensure tables are given increased priority once they begin
+	 * approaching wraparound, we scale the score aggressively if the ages
+	 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
+	 *
+	 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
+	 * less than 105% the value of the respective *_freeze_max_age parameter.
+	 * Note that per-table settings could result in a low score even if the
+	 * table surpasses the failsafe settings.  However, this is a strange
+	 * enough corner case that we don't bother trying to handle it.
+	 *
+	 * We further adjust the effective failsafe ages with the weight
+	 * parameters so that increasing them lowers the ages at which we begin
+	 * scaling aggressively.
+	 */
+	effective_xid_failsafe_age = Max(vacuum_failsafe_age,
+									 autovacuum_freeze_max_age * 1.05);
+	effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
+									  autovacuum_multixact_freeze_max_age * 1.05);
 
-		/*
-		 * To ensure tables are given increased priority once they begin
-		 * approaching wraparound, we scale the score aggressively if the ages
-		 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
-		 *
-		 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
-		 * less than 105% the value of the respective *_freeze_max_age
-		 * parameter.  Note that per-table settings could result in a low
-		 * score even if the table surpasses the failsafe settings.  However,
-		 * this is a strange enough corner case that we don't bother trying to
-		 * handle it.
-		 *
-		 * We further adjust the effective failsafe ages with the weight
-		 * parameters so that increasing them lowers the ages at which we
-		 * begin scaling aggressively.
-		 */
-		effective_xid_failsafe_age = Max(vacuum_failsafe_age,
-										 autovacuum_freeze_max_age * 1.05);
-		effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
-										  autovacuum_multixact_freeze_max_age * 1.05);
+	if (autovacuum_freeze_score_weight > 1.0)
+		effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
+	if (autovacuum_multixact_freeze_score_weight > 1.0)
+		effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
 
-		if (autovacuum_freeze_score_weight > 1.0)
-			effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
-		if (autovacuum_multixact_freeze_score_weight > 1.0)
-			effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
+	if (xid_age >= effective_xid_failsafe_age)
+		scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
+	if (mxid_age >= effective_mxid_failsafe_age)
+		scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
 
-		if (xid_age >= effective_xid_failsafe_age)
-			scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
-		if (mxid_age >= effective_mxid_failsafe_age)
-			scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
+	scores->xid *= autovacuum_freeze_score_weight;
+	scores->mxid *= autovacuum_multixact_freeze_score_weight;
 
-		scores->xid *= autovacuum_freeze_score_weight;
-		scores->mxid *= autovacuum_multixact_freeze_score_weight;
+	scores->max = Max(scores->xid, scores->mxid);
 
-		scores->max = Max(scores->xid, scores->mxid);
+	if (force_vacuum)
+	{
 		*dovacuum = true;
+		scores->is_wraparound = scores->needs_vacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
-		return;
-
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, make a threshold-based decision
+	 * whether to vacuum and/or analyze, and compute the corresponding scores.
+	 * dovacuum/doanalyze are only set when autovacuum is active and enabled
+	 * for this table.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry)
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,15 +3306,17 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Determine if this table needs vacuum, and if it does, update the
+		 * score and mark the table as needing vacuum.
 		 */
 		if (vactuples > vacthresh)
 		{
 			scores->vac = (double) vactuples / Max(vacthresh, 1);
 			scores->vac *= autovacuum_vacuum_score_weight;
 			scores->max = Max(scores->max, scores->vac);
-			*dovacuum = true;
+			scores->needs_vacuum = true;
+			if (av_enabled && AutoVacuumingActive())
+				*dovacuum = true;
 		}
 
 		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
@@ -3320,12 +3324,15 @@ relation_needs_vacanalyze(Oid relid,
 			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
+			scores->needs_vacuum = true;
+			if (av_enabled && AutoVacuumingActive())
+				*dovacuum = true;
 		}
 
 		/*
-		 * Determine if this table needs analyze, and update the score if it
-		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
+		 * Determine if this table needs analyze, and if it does, update the
+		 * score and mark the table as needing analyze. Note that we don't
+		 * analyze TOAST tables and pg_statistic.
 		 */
 		if (anltuples > anlthresh &&
 			relid != StatisticRelationId &&
@@ -3334,7 +3341,9 @@ relation_needs_vacanalyze(Oid relid,
 			scores->anl = (double) anltuples / Max(anlthresh, 1);
 			scores->anl *= autovacuum_analyze_score_weight;
 			scores->max = Max(scores->max, scores->anl);
-			*doanalyze = true;
+			scores->needs_analyze = true;
+			if (av_enabled && AutoVacuumingActive())
+				*doanalyze = true;
 		}
 
 		if (vac_ins_base_thresh >= 0)
-- 
2.47.3



  [application/octet-stream] v5-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch (4.5K, 3-v5-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch)
  download | inline diff:
From d6f25c71e89fb6499f1cbd4d4fdb25c51a415004 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:33:18 +0000
Subject: [PATCH v5 2/4] Add elevel parameter to relation_needs_vacanalyze

Allow callers to control the log level for debug output
by passing an elevel parameter. Passing 0 suppresses
logging. This prepares the function for use by a future
view that should not emit debug messages when queried.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 43 +++++++++++++++++------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 013b49eb69c..ff6ffd4beb1 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -383,7 +383,8 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
-									  AutoVacuumScores *scores);
+									  AutoVacuumScores *scores,
+									  int elevel);
 
 static void autovacuum_do_vac_analyze(autovac_table *tab,
 									  BufferAccessStrategy bstrategy);
@@ -2080,7 +2081,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &scores, DEBUG3);
 
 		/* Relations that need work are added to tables_to_process */
 		if (dovacuum || doanalyze)
@@ -2180,7 +2181,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &scores, DEBUG3);
 
 		/* ignore analyze for toast tables */
 		if (dovacuum)
@@ -2998,7 +2999,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores);
+							  &scores, DEBUG3);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
@@ -3088,6 +3089,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * All fields in AutoVacuumScores are always computed regardless of autovacuum
  * settings.  The dovacuum and doanalyze output parameters are only set when
  * autovacuum is globally active and enabled for the relation.
+ *
+ * elevel controls the log level for debug output.  Pass 0 to suppress logging.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3099,7 +3102,8 @@ relation_needs_vacanalyze(Oid relid,
 						  bool *dovacuum,
 						  bool *doanalyze,
 						  bool *wraparound,
-						  AutoVacuumScores *scores)
+						  AutoVacuumScores *scores,
+						  int elevel)
 {
 	bool		force_vacuum;
 	bool		av_enabled;
@@ -3346,19 +3350,22 @@ relation_needs_vacanalyze(Oid relid,
 				*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+		if (elevel > 0)
+		{
+			if (vac_ins_base_thresh >= 0)
+				elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 instuples, vacinsthresh, scores->vac_ins,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+			else
+				elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+		}
 	}
 }
 
-- 
2.47.3



  [application/octet-stream] v5-0003-Refactor-autovacuum-score-computation-into-comput.patch (4.2K, 4-v5-0003-Refactor-autovacuum-score-computation-into-comput.patch)
  download | inline diff:
From 9b60707b4125630b1869ed5316c6d607db4f8509 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:43:25 +0000
Subject: [PATCH v5 3/4] Refactor autovacuum score computation into
 compute_autovac_score

Autovacuum priority score will be computed for several
reasons: Autovacuum processing and a future monitoring
view to report the scores per table.

This consolidates this code into a new routine
compute_autovac_score() and also removes the need for
recheck_relation_needs_vacanalyze() which is doing the
same thing.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 47 ++++++++++++++---------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index ff6ffd4beb1..a20eb39bf50 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -374,10 +374,11 @@ static void FreeWorkerInfo(int code, Datum arg);
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 											TupleDesc pg_class_desc,
 											int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
-											  Form_pg_class classForm,
-											  int effective_multixact_freeze_max_age,
-											  bool *dovacuum, bool *doanalyze, bool *wraparound);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  AutoVacOpts *relopts, int elevel,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumScores *scores);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
@@ -2836,6 +2837,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	bool		wraparound;
 	AutoVacOpts *avopts;
 	bool		free_avopts = false;
+	AutoVacuumScores scores;
 
 	/* fetch the relation's relcache entry */
 	classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2861,9 +2863,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			avopts = &hentry->ar_reloptions;
 	}
 
-	recheck_relation_needs_vacanalyze(relid, avopts, classForm,
-									  effective_multixact_freeze_max_age,
-									  &dovacuum, &doanalyze, &wraparound);
+	compute_autovac_score(classTup, pg_class_desc,
+						  effective_multixact_freeze_max_age,
+						  avopts, DEBUG3,
+						  &dovacuum, &doanalyze, &wraparound, &priority);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -2973,33 +2976,29 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 }
 
 /*
- * recheck_relation_needs_vacanalyze
- *
- * Subroutine for table_recheck_autovac.
- *
- * Fetch the pgstat of a relation and recheck whether a relation
- * needs to be vacuumed or analyzed.
+ * compute_autovac_score
+ *		Fetch the pgstat entry for a relation and call
+ *		relation_needs_vacanalyze() to determine whether it needs
+ *		vacuum or analyze and compute its priority scores.
  */
 static void
-recheck_relation_needs_vacanalyze(Oid relid,
-								  AutoVacOpts *avopts,
-								  Form_pg_class classForm,
-								  int effective_multixact_freeze_max_age,
-								  bool *dovacuum,
-								  bool *doanalyze,
-								  bool *wraparound)
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  AutoVacOpts *relopts, int elevel,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumScores *scores)
 {
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 	PgStat_StatTabEntry *tabentry;
-	AutoVacuumScores scores;
 
 	/* fetch the pgstat table entry */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
+											  classForm->oid);
 
-	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores, DEBUG3);
+							  scores, elevel);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
-- 
2.47.3



  [application/octet-stream] v5-0004-Add-pg_stat_autovacuum_priority-view.patch (18.8K, 5-v5-0004-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From 98654f40b343a9219ea8f62fb9a490de7e738d82 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v5 4/4] Add pg_stat_autovacuum_priority view

d7965d65f introduced autovacuum prioritization with
scoring. This change adds a view to expose those scores.

The view shows a row per relation indicating whether it
needs vacuum or analyze, the score of each component
and the Max score across all components. This provides
a way to introspect autovacuum priority and provide
feedback on tuning of the related GUCs.

The underlying function pg_stat_get_autovacuum_priority()
scans all relations in the current database and computes
scores using compute_autovac_score(). By default,
only superusers and roles with privileges of
pg_read_all_stats can execute the function, as
controlled by the function's ACL in pg_proc.

The view also emits the relid, namespace and relname,
so it can be joined with other views like
pg_stat_all_tables and pg_stat_progress_vacuum for
complementary vacuum details.

Tests added to vacuum.sql

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 166 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  21 ++++
 src/backend/postmaster/autovacuum.c  |  88 +++++++++++++-
 src/include/catalog/pg_proc.dat      |  10 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  32 ++++++
 src/test/regress/sql/vacuum.sql      |  23 ++++
 8 files changed, 360 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..791850bafe7 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -5256,6 +5265,163 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority. The
+   <structfield>score</structfield>, <structfield>freeze_score</structfield>,
+   and <structfield>multixact_freeze_score</structfield> values may be very large for
+   tables approaching wraparound, as these scores are scaled aggressively
+   once they surpass the failsafe age thresholds.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the vacuum threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the analyze threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>multixact_freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_insert_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_stat_autovacuum_priority</structname> view can
+   be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+
+ </sect2>
+
  <sect2 id="monitoring-stats-functions">
   <title>Statistics Functions</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..30bbd55e330 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,27 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.freeze_score,
+            S.multixact_freeze_score,
+            S.vacuum_score,
+            S.vacuum_insert_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
+REVOKE ALL ON pg_stat_autovacuum_priority FROM PUBLIC;
+GRANT SELECT ON pg_stat_autovacuum_priority TO pg_read_all_stats;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index a20eb39bf50..959905b3b38 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -2866,7 +2868,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	compute_autovac_score(classTup, pg_class_desc,
 						  effective_multixact_freeze_max_age,
 						  avopts, DEBUG3,
-						  &dovacuum, &doanalyze, &wraparound, &priority);
+						  &dovacuum, &doanalyze, &wraparound, &scores);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -3679,3 +3681,87 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *
+ * Returns the autovacuum priority score for a relation as well as if the
+ * relation needs vacuum or analyze, and if the vacuum is a force vacuum
+ * due to wraparound.
+ *
+ * This follows the same setup as do_autovacuum(). Global state such
+ * as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ * computed here, while compute_autovac_score() handles the per-relation
+ * score computation.
+ */
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+#define NUM_AV_SCORE_COLS 10
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+	TableScanDesc relScan;
+	HeapTuple	classTup;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		AutoVacOpts *avopts;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		avopts = extract_autovac_opts(classTup, pg_class_desc);
+
+		compute_autovac_score(classTup, pg_class_desc,
+							  effective_multixact_freeze_max_age, avopts,
+							  0, &dovacuum, &doanalyze,
+							  &wraparound, &scores);
+
+		if (avopts)
+			pfree(avopts);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(scores.needs_vacuum);
+		values[2] = BoolGetDatum(scores.needs_analyze);
+		values[3] = BoolGetDatum(scores.is_wraparound);
+		values[4] = Float8GetDatum(scores.max);
+		values[5] = Float8GetDatum(scores.xid);
+		values[6] = Float8GetDatum(scores.mxid);
+		values[7] = Float8GetDatum(scores.vac);
+		values[8] = Float8GetDatum(scores.vac_ins);
+		values[9] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..6f5d199a506 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,16 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,freeze_score,multixact_freeze_score,vacuum_score,vacuum_insert_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority',
+  proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..53bf9a46e4f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.freeze_score,
+    s.multixact_freeze_score,
+    s.vacuum_score,
+    s.vacuum_insert_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, freeze_score, multixact_freeze_score, vacuum_score, vacuum_insert_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..9cbc2f6a14b 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,35 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_score | vacuum_insert_score | analyze_score 
+--------------+---------------+-------+--------------+---------------------+---------------
+ t            | t             | t     | t            | t                   | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..556fe3127f4 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,26 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-01 18:45                         ` Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-01 18:45 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

v6 attached addresses a silly bug with v5-0001 that Nathan pointed out
offline. v5 was not computing the scores for vacuum,vacuum insert
and analyze thresholds for the !wraparound case.

--
Sami


Attachments:

  [application/octet-stream] v6-0003-Refactor-autovacuum-score-computation-into-comput.patch (4.2K, 2-v6-0003-Refactor-autovacuum-score-computation-into-comput.patch)
  download | inline diff:
From f337ea61a6ce51af4fd999a2f4e823007d0f2ee3 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:43:25 +0000
Subject: [PATCH v6 3/4] Refactor autovacuum score computation into
 compute_autovac_score

Autovacuum priority score will be computed for several
reasons: Autovacuum processing and a future monitoring
view to report the scores per table.

This consolidates this code into a new routine
compute_autovac_score() and also removes the need for
recheck_relation_needs_vacanalyze() which is doing the
same thing.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 47 ++++++++++++++---------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f6e8722a17a..6b2a7e41e53 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -374,10 +374,11 @@ static void FreeWorkerInfo(int code, Datum arg);
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 											TupleDesc pg_class_desc,
 											int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
-											  Form_pg_class classForm,
-											  int effective_multixact_freeze_max_age,
-											  bool *dovacuum, bool *doanalyze, bool *wraparound);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  AutoVacOpts *relopts, int elevel,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumScores *scores);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
@@ -2836,6 +2837,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	bool		wraparound;
 	AutoVacOpts *avopts;
 	bool		free_avopts = false;
+	AutoVacuumScores scores;
 
 	/* fetch the relation's relcache entry */
 	classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2861,9 +2863,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			avopts = &hentry->ar_reloptions;
 	}
 
-	recheck_relation_needs_vacanalyze(relid, avopts, classForm,
-									  effective_multixact_freeze_max_age,
-									  &dovacuum, &doanalyze, &wraparound);
+	compute_autovac_score(classTup, pg_class_desc,
+						  effective_multixact_freeze_max_age,
+						  avopts, DEBUG3,
+						  &dovacuum, &doanalyze, &wraparound, &priority);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -2973,33 +2976,29 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 }
 
 /*
- * recheck_relation_needs_vacanalyze
- *
- * Subroutine for table_recheck_autovac.
- *
- * Fetch the pgstat of a relation and recheck whether a relation
- * needs to be vacuumed or analyzed.
+ * compute_autovac_score
+ *		Fetch the pgstat entry for a relation and call
+ *		relation_needs_vacanalyze() to determine whether it needs
+ *		vacuum or analyze and compute its priority scores.
  */
 static void
-recheck_relation_needs_vacanalyze(Oid relid,
-								  AutoVacOpts *avopts,
-								  Form_pg_class classForm,
-								  int effective_multixact_freeze_max_age,
-								  bool *dovacuum,
-								  bool *doanalyze,
-								  bool *wraparound)
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  AutoVacOpts *relopts, int elevel,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumScores *scores)
 {
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 	PgStat_StatTabEntry *tabentry;
-	AutoVacuumScores scores;
 
 	/* fetch the pgstat table entry */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
+											  classForm->oid);
 
-	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores, DEBUG3);
+							  scores, elevel);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
-- 
2.47.3



  [application/octet-stream] v6-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch (4.5K, 3-v6-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch)
  download | inline diff:
From eed371d2a5d5c7991045a210f5a7314526416f8f Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:33:18 +0000
Subject: [PATCH v6 2/4] Add elevel parameter to relation_needs_vacanalyze

Allow callers to control the log level for debug output
by passing an elevel parameter. Passing 0 suppresses
logging. This prepares the function for use by a future
view that should not emit debug messages when queried.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 43 +++++++++++++++++------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f409a5d9028..f6e8722a17a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -383,7 +383,8 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
-									  AutoVacuumScores *scores);
+									  AutoVacuumScores *scores,
+									  int elevel);
 
 static void autovacuum_do_vac_analyze(autovac_table *tab,
 									  BufferAccessStrategy bstrategy);
@@ -2080,7 +2081,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &scores, DEBUG3);
 
 		/* Relations that need work are added to tables_to_process */
 		if (dovacuum || doanalyze)
@@ -2180,7 +2181,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &scores, DEBUG3);
 
 		/* ignore analyze for toast tables */
 		if (dovacuum)
@@ -2998,7 +2999,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores);
+							  &scores, DEBUG3);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
@@ -3088,6 +3089,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * All fields in AutoVacuumScores are always computed regardless of autovacuum
  * settings.  The dovacuum and doanalyze output parameters are only set when
  * autovacuum is globally active and enabled for the relation.
+ *
+ * elevel controls the log level for debug output.  Pass 0 to suppress logging.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3099,7 +3102,8 @@ relation_needs_vacanalyze(Oid relid,
 						  bool *dovacuum,
 						  bool *doanalyze,
 						  bool *wraparound,
-						  AutoVacuumScores *scores)
+						  AutoVacuumScores *scores,
+						  int elevel)
 {
 	bool		force_vacuum;
 	bool		av_enabled;
@@ -3352,19 +3356,22 @@ relation_needs_vacanalyze(Oid relid,
 				*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+		if (elevel > 0)
+		{
+			if (vac_ins_base_thresh >= 0)
+				elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 instuples, vacinsthresh, scores->vac_ins,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+			else
+				elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+		}
 	}
 }
 
-- 
2.47.3



  [application/octet-stream] v6-0004-Add-pg_stat_autovacuum_priority-view.patch (18.8K, 4-v6-0004-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From 329406e3dbe91811cc41ea1988fd49f96814e274 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v6 4/4] Add pg_stat_autovacuum_priority view

d7965d65f introduced autovacuum prioritization with
scoring. This change adds a view to expose those scores.

The view shows a row per relation indicating whether it
needs vacuum or analyze, the score of each component
and the Max score across all components. This provides
a way to introspect autovacuum priority and provide
feedback on tuning of the related GUCs.

The underlying function pg_stat_get_autovacuum_priority()
scans all relations in the current database and computes
scores using compute_autovac_score(). By default,
only superusers and roles with privileges of
pg_read_all_stats can execute the function, as
controlled by the function's ACL in pg_proc.

The view also emits the relid, namespace and relname,
so it can be joined with other views like
pg_stat_all_tables and pg_stat_progress_vacuum for
complementary vacuum details.

Tests added to vacuum.sql

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 166 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  21 ++++
 src/backend/postmaster/autovacuum.c  |  88 +++++++++++++-
 src/include/catalog/pg_proc.dat      |  10 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  32 ++++++
 src/test/regress/sql/vacuum.sql      |  23 ++++
 8 files changed, 360 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..791850bafe7 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -5256,6 +5265,163 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority. The
+   <structfield>score</structfield>, <structfield>freeze_score</structfield>,
+   and <structfield>multixact_freeze_score</structfield> values may be very large for
+   tables approaching wraparound, as these scores are scaled aggressively
+   once they surpass the failsafe age thresholds.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the vacuum threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the analyze threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>multixact_freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_insert_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_stat_autovacuum_priority</structname> view can
+   be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+
+ </sect2>
+
  <sect2 id="monitoring-stats-functions">
   <title>Statistics Functions</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..30bbd55e330 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,27 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.freeze_score,
+            S.multixact_freeze_score,
+            S.vacuum_score,
+            S.vacuum_insert_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
+REVOKE ALL ON pg_stat_autovacuum_priority FROM PUBLIC;
+GRANT SELECT ON pg_stat_autovacuum_priority TO pg_read_all_stats;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6b2a7e41e53..a02057cf1c6 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -2866,7 +2868,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	compute_autovac_score(classTup, pg_class_desc,
 						  effective_multixact_freeze_max_age,
 						  avopts, DEBUG3,
-						  &dovacuum, &doanalyze, &wraparound, &priority);
+						  &dovacuum, &doanalyze, &wraparound, &scores);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -3685,3 +3687,87 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *
+ * Returns the autovacuum priority score for a relation as well as if the
+ * relation needs vacuum or analyze, and if the vacuum is a force vacuum
+ * due to wraparound.
+ *
+ * This follows the same setup as do_autovacuum(). Global state such
+ * as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ * computed here, while compute_autovac_score() handles the per-relation
+ * score computation.
+ */
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+#define NUM_AV_SCORE_COLS 10
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+	TableScanDesc relScan;
+	HeapTuple	classTup;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		AutoVacOpts *avopts;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		avopts = extract_autovac_opts(classTup, pg_class_desc);
+
+		compute_autovac_score(classTup, pg_class_desc,
+							  effective_multixact_freeze_max_age, avopts,
+							  0, &dovacuum, &doanalyze,
+							  &wraparound, &scores);
+
+		if (avopts)
+			pfree(avopts);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(scores.needs_vacuum);
+		values[2] = BoolGetDatum(scores.needs_analyze);
+		values[3] = BoolGetDatum(scores.is_wraparound);
+		values[4] = Float8GetDatum(scores.max);
+		values[5] = Float8GetDatum(scores.xid);
+		values[6] = Float8GetDatum(scores.mxid);
+		values[7] = Float8GetDatum(scores.vac);
+		values[8] = Float8GetDatum(scores.vac_ins);
+		values[9] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..6f5d199a506 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,16 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,freeze_score,multixact_freeze_score,vacuum_score,vacuum_insert_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority',
+  proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..53bf9a46e4f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.freeze_score,
+    s.multixact_freeze_score,
+    s.vacuum_score,
+    s.vacuum_insert_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, freeze_score, multixact_freeze_score, vacuum_score, vacuum_insert_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..9cbc2f6a14b 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,35 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_score | vacuum_insert_score | analyze_score 
+--------------+---------------+-------+--------------+---------------------+---------------
+ t            | t             | t     | t            | t                   | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..556fe3127f4 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,26 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
-- 
2.47.3



  [application/octet-stream] v6-0001-Always-compute-autovacuum-priority-scores.patch (10.3K, 5-v6-0001-Always-compute-autovacuum-priority-scores.patch)
  download | inline diff:
From 616e02c0e9a2cc5c098387d120751ac8adfa20b1 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:25:30 +0000
Subject: [PATCH v6 1/4] Always compute autovacuum priority scores

Previously, XID/MXID age scores were only computed when
a table was at wraparound risk, and threshold-based
scores were only computed when autovacuum was globally
active. This meant the scores were unavailable for
tables not yet at risk or with autovacuum disabled.

A future patch to monitor the scores and the need
for vacuum/analyze must be able to compute these
values regardless of autovacuum state on the table.
Therefore, separate the need for vacuum and analyze
and track them in AutoVacuumScores so they can be
used for this purpose rather than the output
dovacuum, doanalyze and wraparound parameters that
are acted upon by autovacuum.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 159 +++++++++++++++-------------
 1 file changed, 87 insertions(+), 72 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6694f485216..f409a5d9028 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -318,8 +318,9 @@ static MemoryContext DatabaseListCxt = NULL;
 
 /*
  * This struct is used by relation_needs_vacanalyze() to return the table's
- * score (i.e., the maximum of the component scores) as well as the component
- * scores themselves.
+ * autovacuum priority, including the overall score (i.e., the maximum of the
+ * component scores), the component scores themselves, and whether the table
+ * needs vacuum, analyze, or is at risk of wraparound.
  */
 typedef struct
 {
@@ -329,6 +330,9 @@ typedef struct
 	double		vac;			/* vacuum component */
 	double		vac_ins;		/* vacuum insert component */
 	double		anl;			/* analyze component */
+	bool		needs_vacuum;	/* threshold exceeded for vacuum */
+	bool		needs_analyze;	/* threshold exceeded for analyze */
+	bool		is_wraparound;	/* at risk of XID/MXID wraparound */
 } AutoVacuumScores;
 
 /*
@@ -3080,6 +3084,10 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * The autovacuum table score is returned in scores->max.  The component scores
  * are also returned in the "scores" argument via the other members of the
  * AutoVacuumScores struct.
+ *
+ * All fields in AutoVacuumScores are always computed regardless of autovacuum
+ * settings.  The dovacuum and doanalyze output parameters are only set when
+ * autovacuum is globally active and enabled for the relation.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3122,6 +3130,10 @@ relation_needs_vacanalyze(Oid relid,
 	TransactionId relfrozenxid;
 	MultiXactId relminmxid;
 	MultiXactId multiForceLimit;
+	uint32		xid_age;
+	uint32		mxid_age;
+	int			effective_xid_failsafe_age;
+	int			effective_mxid_failsafe_age;
 
 	Assert(classForm != NULL);
 	Assert(OidIsValid(relid));
@@ -3196,74 +3208,64 @@ relation_needs_vacanalyze(Oid relid,
 	}
 	*wraparound = force_vacuum;
 
-	/* Update the score. */
-	if (force_vacuum)
-	{
-		uint32		xid_age;
-		uint32		mxid_age;
-		int			effective_xid_failsafe_age;
-		int			effective_mxid_failsafe_age;
+	/*
+	 * To calculate the (M)XID age portion of the score, divide the age by its
+	 * respective *_freeze_max_age parameter.
+	 */
+	xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
+	mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
 
-		/*
-		 * To calculate the (M)XID age portion of the score, divide the age by
-		 * its respective *_freeze_max_age parameter.
-		 */
-		xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
-		mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
+	scores->xid = (double) xid_age / freeze_max_age;
+	scores->mxid = (double) mxid_age / multixact_freeze_max_age;
 
-		scores->xid = (double) xid_age / freeze_max_age;
-		scores->mxid = (double) mxid_age / multixact_freeze_max_age;
+	/*
+	 * To ensure tables are given increased priority once they begin
+	 * approaching wraparound, we scale the score aggressively if the ages
+	 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
+	 *
+	 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
+	 * less than 105% the value of the respective *_freeze_max_age parameter.
+	 * Note that per-table settings could result in a low score even if the
+	 * table surpasses the failsafe settings.  However, this is a strange
+	 * enough corner case that we don't bother trying to handle it.
+	 *
+	 * We further adjust the effective failsafe ages with the weight
+	 * parameters so that increasing them lowers the ages at which we begin
+	 * scaling aggressively.
+	 */
+	effective_xid_failsafe_age = Max(vacuum_failsafe_age,
+									 autovacuum_freeze_max_age * 1.05);
+	effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
+									  autovacuum_multixact_freeze_max_age * 1.05);
 
-		/*
-		 * To ensure tables are given increased priority once they begin
-		 * approaching wraparound, we scale the score aggressively if the ages
-		 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
-		 *
-		 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
-		 * less than 105% the value of the respective *_freeze_max_age
-		 * parameter.  Note that per-table settings could result in a low
-		 * score even if the table surpasses the failsafe settings.  However,
-		 * this is a strange enough corner case that we don't bother trying to
-		 * handle it.
-		 *
-		 * We further adjust the effective failsafe ages with the weight
-		 * parameters so that increasing them lowers the ages at which we
-		 * begin scaling aggressively.
-		 */
-		effective_xid_failsafe_age = Max(vacuum_failsafe_age,
-										 autovacuum_freeze_max_age * 1.05);
-		effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
-										  autovacuum_multixact_freeze_max_age * 1.05);
+	if (autovacuum_freeze_score_weight > 1.0)
+		effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
+	if (autovacuum_multixact_freeze_score_weight > 1.0)
+		effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
 
-		if (autovacuum_freeze_score_weight > 1.0)
-			effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
-		if (autovacuum_multixact_freeze_score_weight > 1.0)
-			effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
+	if (xid_age >= effective_xid_failsafe_age)
+		scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
+	if (mxid_age >= effective_mxid_failsafe_age)
+		scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
 
-		if (xid_age >= effective_xid_failsafe_age)
-			scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
-		if (mxid_age >= effective_mxid_failsafe_age)
-			scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
+	scores->xid *= autovacuum_freeze_score_weight;
+	scores->mxid *= autovacuum_multixact_freeze_score_weight;
 
-		scores->xid *= autovacuum_freeze_score_weight;
-		scores->mxid *= autovacuum_multixact_freeze_score_weight;
+	scores->max = Max(scores->xid, scores->mxid);
 
-		scores->max = Max(scores->xid, scores->mxid);
+	if (force_vacuum)
+	{
 		*dovacuum = true;
+		scores->is_wraparound = scores->needs_vacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
-		return;
-
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, make a threshold-based decision
+	 * whether to vacuum and/or analyze, and compute the corresponding scores.
+	 * dovacuum/doanalyze are only set when autovacuum is active and enabled
+	 * for this table.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry)
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,37 +3306,50 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Update the vacuum score and determine if this table requires
+		 * vacuuming.
 		 */
+		scores->vac = (double) vactuples / Max(vacthresh, 1);
+		scores->vac *= autovacuum_vacuum_score_weight;
+		scores->max = Max(scores->max, scores->vac);
 		if (vactuples > vacthresh)
 		{
-			scores->vac = (double) vactuples / Max(vacthresh, 1);
-			scores->vac *= autovacuum_vacuum_score_weight;
-			scores->max = Max(scores->max, scores->vac);
-			*dovacuum = true;
+			scores->needs_vacuum = true;
+			if (av_enabled && AutoVacuumingActive())
+				*dovacuum = true;
 		}
 
-		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+		/*
+		 * Ditto for vacuum insert score when it is applicable.
+		 */
+		if (vac_ins_base_thresh >= 0)
 		{
 			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
+
+			if (instuples > vacinsthresh)
+			{
+				scores->needs_vacuum = true;
+				if (av_enabled && AutoVacuumingActive())
+					*dovacuum = true;
+			}
 		}
 
 		/*
-		 * Determine if this table needs analyze, and update the score if it
-		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
+		 * Update the analyze scores and determine if this table requires
+		 * analyze. Note that we don't analyze TOAST tables and pg_statistic.
 		 */
+		scores->anl = (double) anltuples / Max(anlthresh, 1);
+		scores->anl *= autovacuum_analyze_score_weight;
+		scores->max = Max(scores->max, scores->anl);
 		if (anltuples > anlthresh &&
 			relid != StatisticRelationId &&
 			classForm->relkind != RELKIND_TOASTVALUE)
 		{
-			scores->anl = (double) anltuples / Max(anlthresh, 1);
-			scores->anl *= autovacuum_analyze_score_weight;
-			scores->max = Max(scores->max, scores->anl);
-			*doanalyze = true;
+			scores->needs_analyze = true;
+			if (av_enabled && AutoVacuumingActive())
+				*doanalyze = true;
 		}
 
 		if (vac_ins_base_thresh >= 0)
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-01 21:28                           ` Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-01 21:28 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

+    bool        needs_vacuum;   /* threshold exceeded for vacuum */
+    bool        needs_analyze;  /* threshold exceeded for analyze */
+    bool        is_wraparound;  /* at risk of XID/MXID wraparound */

I've been thinking about whether to also return whether autovacuum is
enabled in the view, i.e., AutoVacuumingActive() && av_enabled.  My
instinct was that would help explain why autovacuum isn't picking up tables
that are eligible for autovacuum based on the view.  One wrinkle is that
it'd report false even when autovacuum is working on a table for wraparound
prevention.  I'm also not especially excited about further complicating
this stuff for folks who disable autovacuum.  Furthermore, the value of the
reloptions and autovacuum GUC are discoverable elsewhere.  So, I'm
currently leaning towards leaving that information out for now.

+        if (vactuples > vacthresh)
+        {
+            scores->needs_vacuum = true;
+            if (av_enabled && AutoVacuumingActive())
                 *dovacuum = true;
         }

nitpick:  We might be able to simplify this a bit by 1) storing "av_enabled
&& AutoVacuumingActive()" in a variable and 2) reworking the code to look
more like this:

	scores->needs_vacuum = (vactuples > vacthresh);
	*do_vacuum |= (av_enabled && scores->needs_vacuum);

... but others might find your version easier to read.

Otherwise, 0001 looks good.

In 0003, I think you missed renaming the last argument to
compute_autovac_score() in table_recheck_autovac().

I didn't see anything else in this read-through.  I'm planning to start
preparing this for commit tomorrow.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-01 23:04                             ` Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-01 23:04 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> I've been thinking about whether to also return whether autovacuum is
> enabled in the view, i.e., AutoVacuumingActive() && av_enabled.

I don't think we can rely on AutoVacuumingActive() being stable since a
backend that does a SET track_counts = off for whatever reason and
then calls the view will get false. av_enabled will likely be the only
thing we can report.

> So, I'm
> currently leaning towards leaving that information out for now.

I agree.

>
>         scores->needs_vacuum = (vactuples > vacthresh);
>         *do_vacuum |= (av_enabled && scores->needs_vacuum);
>
> ... but others might find your version easier to read.

yeah, for readability, I'll stick with the current.

> Otherwise, 0001 looks good.
>
> In 0003, I think you missed renaming the last argument to
> compute_autovac_score() in table_recheck_autovac().

Earlier, I did not compile with 0003 only. Fixed.

> I didn't see anything else in this read-through.  I'm planning to start
> preparing this for commit tomorrow.

Thanks! here is v7

--
Sami


Attachments:

  [application/octet-stream] v7-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch (4.5K, 2-v7-0002-Add-elevel-parameter-to-relation_needs_vacanalyze.patch)
  download | inline diff:
From ee4488bef0dc28f9ee1327bdeeacf7e231da3470 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:33:18 +0000
Subject: [PATCH v7 2/4] Add elevel parameter to relation_needs_vacanalyze

Allow callers to control the log level for debug output
by passing an elevel parameter. Passing 0 suppresses
logging. This prepares the function for use by a future
view that should not emit debug messages when queried.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 43 +++++++++++++++++------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f409a5d9028..f6e8722a17a 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -383,7 +383,8 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
-									  AutoVacuumScores *scores);
+									  AutoVacuumScores *scores,
+									  int elevel);
 
 static void autovacuum_do_vac_analyze(autovac_table *tab,
 									  BufferAccessStrategy bstrategy);
@@ -2080,7 +2081,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &scores, DEBUG3);
 
 		/* Relations that need work are added to tables_to_process */
 		if (dovacuum || doanalyze)
@@ -2180,7 +2181,7 @@ do_autovacuum(void)
 		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
 								  effective_multixact_freeze_max_age,
 								  &dovacuum, &doanalyze, &wraparound,
-								  &scores);
+								  &scores, DEBUG3);
 
 		/* ignore analyze for toast tables */
 		if (dovacuum)
@@ -2998,7 +2999,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
 	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores);
+							  &scores, DEBUG3);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
@@ -3088,6 +3089,8 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * All fields in AutoVacuumScores are always computed regardless of autovacuum
  * settings.  The dovacuum and doanalyze output parameters are only set when
  * autovacuum is globally active and enabled for the relation.
+ *
+ * elevel controls the log level for debug output.  Pass 0 to suppress logging.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3099,7 +3102,8 @@ relation_needs_vacanalyze(Oid relid,
 						  bool *dovacuum,
 						  bool *doanalyze,
 						  bool *wraparound,
-						  AutoVacuumScores *scores)
+						  AutoVacuumScores *scores,
+						  int elevel)
 {
 	bool		force_vacuum;
 	bool		av_enabled;
@@ -3352,19 +3356,22 @@ relation_needs_vacanalyze(Oid relid,
 				*doanalyze = true;
 		}
 
-		if (vac_ins_base_thresh >= 0)
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 instuples, vacinsthresh, scores->vac_ins,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
-		else
-			elog(DEBUG3, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
-				 NameStr(classForm->relname),
-				 vactuples, vacthresh, scores->vac,
-				 anltuples, anlthresh, scores->anl,
-				 scores->xid, scores->mxid);
+		if (elevel > 0)
+		{
+			if (vac_ins_base_thresh >= 0)
+				elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: %.0f (thresh %.0f, score %.2f), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 instuples, vacinsthresh, scores->vac_ins,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+			else
+				elog(elevel, "%s: vac: %.0f (thresh %.0f, score %.2f), ins: (disabled), anl: %.0f (thresh %.0f, score %.2f), xid score: %.2f, mxid score: %.2f",
+					 NameStr(classForm->relname),
+					 vactuples, vacthresh, scores->vac,
+					 anltuples, anlthresh, scores->anl,
+					 scores->xid, scores->mxid);
+		}
 	}
 }
 
-- 
2.47.3



  [application/octet-stream] v7-0004-Add-pg_stat_autovacuum_priority-view.patch (18.4K, 3-v7-0004-Add-pg_stat_autovacuum_priority-view.patch)
  download | inline diff:
From 45ceac183547ed8e4f090f2efd97326015483f31 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Mon, 23 Mar 2026 17:03:59 +0000
Subject: [PATCH v7 4/4] Add pg_stat_autovacuum_priority view

d7965d65f introduced autovacuum prioritization with
scoring. This change adds a view to expose those scores.

The view shows a row per relation indicating whether it
needs vacuum or analyze, the score of each component
and the Max score across all components. This provides
a way to introspect autovacuum priority and provide
feedback on tuning of the related GUCs.

The underlying function pg_stat_get_autovacuum_priority()
scans all relations in the current database and computes
scores using compute_autovac_score(). By default,
only superusers and roles with privileges of
pg_read_all_stats can execute the function, as
controlled by the function's ACL in pg_proc.

The view also emits the relid, namespace and relname,
so it can be joined with other views like
pg_stat_all_tables and pg_stat_progress_vacuum for
complementary vacuum details.

Tests added to vacuum.sql

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 doc/src/sgml/maintenance.sgml        |   6 +
 doc/src/sgml/monitoring.sgml         | 166 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  21 ++++
 src/backend/postmaster/autovacuum.c  |  86 ++++++++++++++
 src/include/catalog/pg_proc.dat      |  10 ++
 src/test/regress/expected/rules.out  |  15 +++
 src/test/regress/expected/vacuum.out |  32 ++++++
 src/test/regress/sql/vacuum.sql      |  23 ++++
 8 files changed, 359 insertions(+)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..2125774aff3 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,12 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+     <structname>pg_stat_autovacuum_priority</structname></link> view can be
+     used to inspect each table's autovacuum need and priority score.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index bb75ed1069b..791850bafe7 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -463,6 +463,15 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_priority</structname><indexterm><primary>pg_stat_autovacuum_priority</primary></indexterm></entry>
+      <entry>One row per relation in the current database, showing
+       a table's autovacuum need and priority. See
+       <link linkend="monitoring-pg-stat-autovacuum-priority-view">
+       <structname>pg_stat_autovacuum_priority</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_bgwriter</structname><indexterm><primary>pg_stat_bgwriter</primary></indexterm></entry>
       <entry>One row only, showing statistics about the
@@ -5256,6 +5265,163 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-priority-view">
+  <title><structname>pg_stat_autovacuum_priority</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_priority</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_priority</structname> view contains
+   one row per relation in the current database, showing whether a table
+   needs autovacuum or autoanalyze and its priority. The
+   <structfield>score</structfield>, <structfield>freeze_score</structfield>,
+   and <structfield>multixact_freeze_score</structfield> values may be very large for
+   tables approaching wraparound, as these scores are scaled aggressively
+   once they surpass the failsafe age thresholds.
+  </para>
+
+  <table id="pg-stat-autovacuum-priority-view" xreflabel="pg_stat_autovacuum_priority">
+   <title><structname>pg_stat_autovacuum_priority</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       OID of a table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that this table is in
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the vacuum threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table exceeds the analyze threshold
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>wraparound</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if vacuuming is needed to prevent transaction ID or
+       multixact ID wraparound
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Priority score used by autovacuum to order which tables to
+       process first. Higher values indicate greater urgency. This is
+       the maximum of all component scores below.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on transaction ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>multixact_freeze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on multixact ID age.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the estimated number of dead tuples
+       needing removal by vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_insert_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of inserts since the last
+       vacuum.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Score component based on the number of modifications since the
+       last analyze.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_stat_autovacuum_priority</structname> view can
+   be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+
+ </sect2>
+
  <sect2 id="monitoring-stats-functions">
   <title>Statistics Functions</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e54018004db..30bbd55e330 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,27 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_priority AS
+    SELECT
+            S.relid,
+            N.nspname AS schemaname,
+            C.relname AS relname,
+            S.needs_vacuum,
+            S.needs_analyze,
+            S.wraparound,
+            S.score,
+            S.freeze_score,
+            S.multixact_freeze_score,
+            S.vacuum_score,
+            S.vacuum_insert_score,
+            S.analyze_score
+    FROM pg_stat_get_autovacuum_priority() S
+         JOIN pg_class C ON C.oid = S.relid
+         LEFT JOIN pg_namespace N ON N.oid = C.relnamespace;
+
+REVOKE ALL ON pg_stat_autovacuum_priority FROM PUBLIC;
+GRANT SELECT ON pg_stat_autovacuum_priority TO pg_read_all_stats;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 090b87fa41c..a02057cf1c6 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -3685,3 +3687,87 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * pg_stat_get_autovacuum_priority
+ *
+ * Returns the autovacuum priority score for a relation as well as if the
+ * relation needs vacuum or analyze, and if the vacuum is a force vacuum
+ * due to wraparound.
+ *
+ * This follows the same setup as do_autovacuum(). Global state such
+ * as recentXid/recentMulti and effective_multixact_freeze_max_age is
+ * computed here, while compute_autovac_score() handles the per-relation
+ * score computation.
+ */
+Datum
+pg_stat_get_autovacuum_priority(PG_FUNCTION_ARGS)
+{
+#define NUM_AV_SCORE_COLS 10
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Relation	classRel;
+	TupleDesc	pg_class_desc;
+	int			effective_multixact_freeze_max_age;
+	TableScanDesc relScan;
+	HeapTuple	classTup;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	classRel = table_open(RelationRelationId, AccessShareLock);
+	pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
+
+	relScan = table_beginscan_catalog(classRel, 0, NULL);
+	while ((classTup = heap_getnext(relScan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(classTup);
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		AutoVacOpts *avopts;
+		Datum		values[NUM_AV_SCORE_COLS];
+		bool		nulls[NUM_AV_SCORE_COLS] = {false};
+
+		if (classForm->relkind != RELKIND_RELATION &&
+			classForm->relkind != RELKIND_MATVIEW &&
+			classForm->relkind != RELKIND_TOASTVALUE)
+			continue;
+
+		if (classForm->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		avopts = extract_autovac_opts(classTup, pg_class_desc);
+
+		compute_autovac_score(classTup, pg_class_desc,
+							  effective_multixact_freeze_max_age, avopts,
+							  0, &dovacuum, &doanalyze,
+							  &wraparound, &scores);
+
+		if (avopts)
+			pfree(avopts);
+
+		values[0] = ObjectIdGetDatum(classForm->oid);
+		values[1] = BoolGetDatum(scores.needs_vacuum);
+		values[2] = BoolGetDatum(scores.needs_analyze);
+		values[3] = BoolGetDatum(scores.is_wraparound);
+		values[4] = Float8GetDatum(scores.max);
+		values[5] = Float8GetDatum(scores.xid);
+		values[6] = Float8GetDatum(scores.mxid);
+		values[7] = Float8GetDatum(scores.vac);
+		values[8] = Float8GetDatum(scores.vac_ins);
+		values[9] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+	table_endscan(relScan);
+
+	table_close(classRel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..6f5d199a506 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,16 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409',
+  descr => 'statistics: autovacuum priority scores for all relations',
+  proname => 'pg_stat_get_autovacuum_priority', prorows => '100',
+  proretset => 't', provolatile => 'v', proparallel => 'r',
+  prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{relid,needs_vacuum,needs_analyze,wraparound,score,freeze_score,multixact_freeze_score,vacuum_score,vacuum_insert_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_priority',
+  proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..53bf9a46e4f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,21 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_priority| SELECT s.relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.wraparound,
+    s.score,
+    s.freeze_score,
+    s.multixact_freeze_score,
+    s.vacuum_score,
+    s.vacuum_insert_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_priority() s(relid, needs_vacuum, needs_analyze, wraparound, score, freeze_score, multixact_freeze_score, vacuum_score, vacuum_insert_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.relid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..9cbc2f6a14b 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -730,3 +730,35 @@ SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
 (1 row)
 
 DROP TABLE vac_rewrite_toast;
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+ needs_vacuum | needs_analyze | score | vacuum_score | vacuum_insert_score | analyze_score 
+--------------+---------------+-------+--------------+---------------------+---------------
+ t            | t             | t     | t            | t                   | t
+(1 row)
+
+DROP TABLE vacuum_priority_test;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..556fe3127f4 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -525,3 +525,26 @@ SELECT id, pg_column_toast_chunk_id(f1) IS NULL AS f1_chunk_null,
 SELECT pg_column_toast_chunk_id(f1) = :'id_2_chunk' AS same_chunk
   FROM vac_rewrite_toast WHERE id = 2;
 DROP TABLE vac_rewrite_toast;
+
+-- Test pg_stat_autovacuum_priority view. Scores are checked to be
+-- within an expected range. freeze_score and multixact_freeze_score are excluded
+-- as they require consuming enough XIDs to be meaningful.
+CREATE TABLE vacuum_priority_test (id int)
+  WITH (autovacuum_analyze_threshold = 1,
+        autovacuum_vacuum_threshold = 1,
+        autovacuum_vacuum_insert_threshold = 1,
+        autovacuum_enabled = off);
+INSERT INTO vacuum_priority_test SELECT 1;
+INSERT INTO vacuum_priority_test SELECT 2;
+DELETE FROM vacuum_priority_test WHERE id = 1;
+DELETE FROM vacuum_priority_test WHERE id = 2;
+-- force vacuum stats to be flushed
+SELECT pg_stat_force_next_flush();
+SELECT needs_vacuum, needs_analyze,
+  score > 0 AND score <= 4 AS score,
+  vacuum_score > 0 AND vacuum_score <= 2 AS vacuum_score,
+  vacuum_insert_score > 0 AND vacuum_insert_score <= 2 AS vacuum_insert_score,
+  analyze_score > 0 AND analyze_score <= 4 AS analyze_score
+  FROM pg_stat_autovacuum_priority
+  WHERE relname = 'vacuum_priority_test';
+DROP TABLE vacuum_priority_test;
-- 
2.47.3



  [application/octet-stream] v7-0001-Always-compute-autovacuum-priority-scores.patch (10.3K, 4-v7-0001-Always-compute-autovacuum-priority-scores.patch)
  download | inline diff:
From e1ea82c8c7a3e6129dd820481854e0ecda6c694f Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:25:30 +0000
Subject: [PATCH v7 1/4] Always compute autovacuum priority scores

Previously, XID/MXID age scores were only computed when
a table was at wraparound risk, and threshold-based
scores were only computed when autovacuum was globally
active. This meant the scores were unavailable for
tables not yet at risk or with autovacuum disabled.

A future patch to monitor the scores and the need
for vacuum/analyze must be able to compute these
values regardless of autovacuum state on the table.
Therefore, separate the need for vacuum and analyze
and track them in AutoVacuumScores so they can be
used for this purpose rather than the output
dovacuum, doanalyze and wraparound parameters that
are acted upon by autovacuum.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 159 +++++++++++++++-------------
 1 file changed, 87 insertions(+), 72 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6694f485216..f409a5d9028 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -318,8 +318,9 @@ static MemoryContext DatabaseListCxt = NULL;
 
 /*
  * This struct is used by relation_needs_vacanalyze() to return the table's
- * score (i.e., the maximum of the component scores) as well as the component
- * scores themselves.
+ * autovacuum priority, including the overall score (i.e., the maximum of the
+ * component scores), the component scores themselves, and whether the table
+ * needs vacuum, analyze, or is at risk of wraparound.
  */
 typedef struct
 {
@@ -329,6 +330,9 @@ typedef struct
 	double		vac;			/* vacuum component */
 	double		vac_ins;		/* vacuum insert component */
 	double		anl;			/* analyze component */
+	bool		needs_vacuum;	/* threshold exceeded for vacuum */
+	bool		needs_analyze;	/* threshold exceeded for analyze */
+	bool		is_wraparound;	/* at risk of XID/MXID wraparound */
 } AutoVacuumScores;
 
 /*
@@ -3080,6 +3084,10 @@ recheck_relation_needs_vacanalyze(Oid relid,
  * The autovacuum table score is returned in scores->max.  The component scores
  * are also returned in the "scores" argument via the other members of the
  * AutoVacuumScores struct.
+ *
+ * All fields in AutoVacuumScores are always computed regardless of autovacuum
+ * settings.  The dovacuum and doanalyze output parameters are only set when
+ * autovacuum is globally active and enabled for the relation.
  */
 static void
 relation_needs_vacanalyze(Oid relid,
@@ -3122,6 +3130,10 @@ relation_needs_vacanalyze(Oid relid,
 	TransactionId relfrozenxid;
 	MultiXactId relminmxid;
 	MultiXactId multiForceLimit;
+	uint32		xid_age;
+	uint32		mxid_age;
+	int			effective_xid_failsafe_age;
+	int			effective_mxid_failsafe_age;
 
 	Assert(classForm != NULL);
 	Assert(OidIsValid(relid));
@@ -3196,74 +3208,64 @@ relation_needs_vacanalyze(Oid relid,
 	}
 	*wraparound = force_vacuum;
 
-	/* Update the score. */
-	if (force_vacuum)
-	{
-		uint32		xid_age;
-		uint32		mxid_age;
-		int			effective_xid_failsafe_age;
-		int			effective_mxid_failsafe_age;
+	/*
+	 * To calculate the (M)XID age portion of the score, divide the age by its
+	 * respective *_freeze_max_age parameter.
+	 */
+	xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
+	mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
 
-		/*
-		 * To calculate the (M)XID age portion of the score, divide the age by
-		 * its respective *_freeze_max_age parameter.
-		 */
-		xid_age = TransactionIdIsNormal(relfrozenxid) ? recentXid - relfrozenxid : 0;
-		mxid_age = MultiXactIdIsValid(relminmxid) ? recentMulti - relminmxid : 0;
+	scores->xid = (double) xid_age / freeze_max_age;
+	scores->mxid = (double) mxid_age / multixact_freeze_max_age;
 
-		scores->xid = (double) xid_age / freeze_max_age;
-		scores->mxid = (double) mxid_age / multixact_freeze_max_age;
+	/*
+	 * To ensure tables are given increased priority once they begin
+	 * approaching wraparound, we scale the score aggressively if the ages
+	 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
+	 *
+	 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
+	 * less than 105% the value of the respective *_freeze_max_age parameter.
+	 * Note that per-table settings could result in a low score even if the
+	 * table surpasses the failsafe settings.  However, this is a strange
+	 * enough corner case that we don't bother trying to handle it.
+	 *
+	 * We further adjust the effective failsafe ages with the weight
+	 * parameters so that increasing them lowers the ages at which we begin
+	 * scaling aggressively.
+	 */
+	effective_xid_failsafe_age = Max(vacuum_failsafe_age,
+									 autovacuum_freeze_max_age * 1.05);
+	effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
+									  autovacuum_multixact_freeze_max_age * 1.05);
 
-		/*
-		 * To ensure tables are given increased priority once they begin
-		 * approaching wraparound, we scale the score aggressively if the ages
-		 * surpass vacuum_failsafe_age or vacuum_multixact_failsafe_age.
-		 *
-		 * As in vacuum_xid_failsafe_check(), the effective failsafe age is no
-		 * less than 105% the value of the respective *_freeze_max_age
-		 * parameter.  Note that per-table settings could result in a low
-		 * score even if the table surpasses the failsafe settings.  However,
-		 * this is a strange enough corner case that we don't bother trying to
-		 * handle it.
-		 *
-		 * We further adjust the effective failsafe ages with the weight
-		 * parameters so that increasing them lowers the ages at which we
-		 * begin scaling aggressively.
-		 */
-		effective_xid_failsafe_age = Max(vacuum_failsafe_age,
-										 autovacuum_freeze_max_age * 1.05);
-		effective_mxid_failsafe_age = Max(vacuum_multixact_failsafe_age,
-										  autovacuum_multixact_freeze_max_age * 1.05);
+	if (autovacuum_freeze_score_weight > 1.0)
+		effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
+	if (autovacuum_multixact_freeze_score_weight > 1.0)
+		effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
 
-		if (autovacuum_freeze_score_weight > 1.0)
-			effective_xid_failsafe_age /= autovacuum_freeze_score_weight;
-		if (autovacuum_multixact_freeze_score_weight > 1.0)
-			effective_mxid_failsafe_age /= autovacuum_multixact_freeze_score_weight;
+	if (xid_age >= effective_xid_failsafe_age)
+		scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
+	if (mxid_age >= effective_mxid_failsafe_age)
+		scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
 
-		if (xid_age >= effective_xid_failsafe_age)
-			scores->xid = pow(scores->xid, Max(1.0, (double) xid_age / 100000000));
-		if (mxid_age >= effective_mxid_failsafe_age)
-			scores->mxid = pow(scores->mxid, Max(1.0, (double) mxid_age / 100000000));
+	scores->xid *= autovacuum_freeze_score_weight;
+	scores->mxid *= autovacuum_multixact_freeze_score_weight;
 
-		scores->xid *= autovacuum_freeze_score_weight;
-		scores->mxid *= autovacuum_multixact_freeze_score_weight;
+	scores->max = Max(scores->xid, scores->mxid);
 
-		scores->max = Max(scores->xid, scores->mxid);
+	if (force_vacuum)
+	{
 		*dovacuum = true;
+		scores->is_wraparound = scores->needs_vacuum = true;
 	}
 
-	/* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
-	if (!av_enabled && !force_vacuum)
-		return;
-
 	/*
-	 * If we found stats for the table, and autovacuum is currently enabled,
-	 * make a threshold-based decision whether to vacuum and/or analyze.  If
-	 * autovacuum is currently disabled, we must be here for anti-wraparound
-	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
-	 * forced.
+	 * If we found stats for the table, make a threshold-based decision
+	 * whether to vacuum and/or analyze, and compute the corresponding scores.
+	 * dovacuum/doanalyze are only set when autovacuum is active and enabled
+	 * for this table.
 	 */
-	if (tabentry && AutoVacuumingActive())
+	if (tabentry)
 	{
 		float4		pcnt_unfrozen = 1;
 		float4		reltuples = classForm->reltuples;
@@ -3304,37 +3306,50 @@ relation_needs_vacanalyze(Oid relid,
 		anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;
 
 		/*
-		 * Determine if this table needs vacuum, and update the score if it
-		 * does.
+		 * Update the vacuum score and determine if this table requires
+		 * vacuuming.
 		 */
+		scores->vac = (double) vactuples / Max(vacthresh, 1);
+		scores->vac *= autovacuum_vacuum_score_weight;
+		scores->max = Max(scores->max, scores->vac);
 		if (vactuples > vacthresh)
 		{
-			scores->vac = (double) vactuples / Max(vacthresh, 1);
-			scores->vac *= autovacuum_vacuum_score_weight;
-			scores->max = Max(scores->max, scores->vac);
-			*dovacuum = true;
+			scores->needs_vacuum = true;
+			if (av_enabled && AutoVacuumingActive())
+				*dovacuum = true;
 		}
 
-		if (vac_ins_base_thresh >= 0 && instuples > vacinsthresh)
+		/*
+		 * Ditto for vacuum insert score when it is applicable.
+		 */
+		if (vac_ins_base_thresh >= 0)
 		{
 			scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 			scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 			scores->max = Max(scores->max, scores->vac_ins);
-			*dovacuum = true;
+
+			if (instuples > vacinsthresh)
+			{
+				scores->needs_vacuum = true;
+				if (av_enabled && AutoVacuumingActive())
+					*dovacuum = true;
+			}
 		}
 
 		/*
-		 * Determine if this table needs analyze, and update the score if it
-		 * does.  Note that we don't analyze TOAST tables and pg_statistic.
+		 * Update the analyze scores and determine if this table requires
+		 * analyze. Note that we don't analyze TOAST tables and pg_statistic.
 		 */
+		scores->anl = (double) anltuples / Max(anlthresh, 1);
+		scores->anl *= autovacuum_analyze_score_weight;
+		scores->max = Max(scores->max, scores->anl);
 		if (anltuples > anlthresh &&
 			relid != StatisticRelationId &&
 			classForm->relkind != RELKIND_TOASTVALUE)
 		{
-			scores->anl = (double) anltuples / Max(anlthresh, 1);
-			scores->anl *= autovacuum_analyze_score_weight;
-			scores->max = Max(scores->max, scores->anl);
-			*doanalyze = true;
+			scores->needs_analyze = true;
+			if (av_enabled && AutoVacuumingActive())
+				*doanalyze = true;
 		}
 
 		if (vac_ins_base_thresh >= 0)
-- 
2.47.3



  [application/octet-stream] v7-0003-Refactor-autovacuum-score-computation-into-comput.patch (4.2K, 5-v7-0003-Refactor-autovacuum-score-computation-into-comput.patch)
  download | inline diff:
From 0c70bb896dfe80916e003f5d28e82605e3b883fa Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Tue, 31 Mar 2026 18:43:25 +0000
Subject: [PATCH v7 3/4] Refactor autovacuum score computation into
 compute_autovac_score

Autovacuum priority score will be computed for several
reasons: Autovacuum processing and a future monitoring
view to report the scores per table.

This consolidates this code into a new routine
compute_autovac_score() and also removes the need for
recheck_relation_needs_vacanalyze() which is doing the
same thing.

Discussion: https://postgr.es/m/CAA5RZ0s4xjMrB-VAnLccC7kY8d0-4806-Lsac-czJsdA1LXtAw%40mail.gmail.com
---
 src/backend/postmaster/autovacuum.c | 47 ++++++++++++++---------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index f6e8722a17a..090b87fa41c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -374,10 +374,11 @@ static void FreeWorkerInfo(int code, Datum arg);
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 											TupleDesc pg_class_desc,
 											int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
-											  Form_pg_class classForm,
-											  int effective_multixact_freeze_max_age,
-											  bool *dovacuum, bool *doanalyze, bool *wraparound);
+static void compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+								  int effective_multixact_freeze_max_age,
+								  AutoVacOpts *relopts, int elevel,
+								  bool *dovacuum, bool *doanalyze,
+								  bool *wraparound, AutoVacuumScores *scores);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
 									  PgStat_StatTabEntry *tabentry,
@@ -2836,6 +2837,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	bool		wraparound;
 	AutoVacOpts *avopts;
 	bool		free_avopts = false;
+	AutoVacuumScores scores;
 
 	/* fetch the relation's relcache entry */
 	classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2861,9 +2863,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			avopts = &hentry->ar_reloptions;
 	}
 
-	recheck_relation_needs_vacanalyze(relid, avopts, classForm,
-									  effective_multixact_freeze_max_age,
-									  &dovacuum, &doanalyze, &wraparound);
+	compute_autovac_score(classTup, pg_class_desc,
+						  effective_multixact_freeze_max_age,
+						  avopts, DEBUG3,
+						  &dovacuum, &doanalyze, &wraparound, &scores);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -2973,33 +2976,29 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 }
 
 /*
- * recheck_relation_needs_vacanalyze
- *
- * Subroutine for table_recheck_autovac.
- *
- * Fetch the pgstat of a relation and recheck whether a relation
- * needs to be vacuumed or analyzed.
+ * compute_autovac_score
+ *		Fetch the pgstat entry for a relation and call
+ *		relation_needs_vacanalyze() to determine whether it needs
+ *		vacuum or analyze and compute its priority scores.
  */
 static void
-recheck_relation_needs_vacanalyze(Oid relid,
-								  AutoVacOpts *avopts,
-								  Form_pg_class classForm,
-								  int effective_multixact_freeze_max_age,
-								  bool *dovacuum,
-								  bool *doanalyze,
-								  bool *wraparound)
+compute_autovac_score(HeapTuple tuple, TupleDesc pg_class_desc,
+					  int effective_multixact_freeze_max_age,
+					  AutoVacOpts *relopts, int elevel,
+					  bool *dovacuum, bool *doanalyze,
+					  bool *wraparound, AutoVacuumScores *scores)
 {
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 	PgStat_StatTabEntry *tabentry;
-	AutoVacuumScores scores;
 
 	/* fetch the pgstat table entry */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
+											  classForm->oid);
 
-	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
+	relation_needs_vacanalyze(classForm->oid, relopts, classForm, tabentry,
 							  effective_multixact_freeze_max_age,
 							  dovacuum, doanalyze, wraparound,
-							  &scores, DEBUG3);
+							  scores, elevel);
 
 	/* Release tabentry to avoid leakage */
 	if (tabentry)
-- 
2.47.3



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-03 16:47                               ` Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-03 16:47 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

On Wed, Apr 01, 2026 at 06:04:36PM -0500, Sami Imseih wrote:
> Thanks! here is v7

Alright, I've been preparing these for commit.  Most changes are cosmetic,
but there are a couple of bigger ones I should note:

* I added a prerequisite patch for relation_needs_vacanalyze() that saves a
level of indentation on a chunk of code.  This simplifies 0001 (now 0002) a
bit.

* I noticed that if autovacuum decides to force a vacuum for
anti-wraparound purposes, it might also decide to analyze the table even if
autovacuum is disabled for it.  AFAICT this is accidental, but since it's
behaved this way since commit 48188e1621 (2006) [0], I am slightly worried
that this bug may have become a feature.  In 0002, I separated this edge
case in the code and added a comment, and I intend to start a new thread
about removing it.

* I removed the booleans in the view in favor of just noting that scores >=
1.0 means the table needs processing.  IMHO trying to distinguish
needs_vacuum from do_vacuum is just going to confuse folks more than
anything, and IIUC this information is redundant with "score >= 1.0",
anyway.

* I renamed the view to pg_autovacuum_scores.  While some of the
information in the view depends on cumulative statistics, not all of it
does, and what does is quite heavily modified from the original stats.  So,
I didn't think the pg_stat_* prefix was appropriate, although I can see how
reasonable people might disagree.

* I considered whether to make the backing function per-table and
ultimately decided against it.  The initialization logic is a bit
expensive, and I assume most folks will be interested in the full picture
of the current database.  Maybe we could add a per-table function down the
road, but I don't see any strong need for that for now.

I'm planning to commit 0001-0004 this afternoon, assuming cfbot is happy.
I'm hoping to commit 0005 on Monday or Tuesday.  Please take a look at v8
if you have time.

[0] https://postgr.es/m/23710.1162661716%40sss.pgh.pa.us

-- 
nathan


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-03 19:13                                 ` Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-03 19:13 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> Alright, I've been preparing these for commit.  Most changes are cosmetic,
> but there are a couple of bigger ones I should note:

Thanks!

> * I added a prerequisite patch for relation_needs_vacanalyze() that saves a
> level of indentation on a chunk of code.  This simplifies 0001 (now 0002) a
> bit.

I like this this.

> * I noticed that if autovacuum decides to force a vacuum for
> anti-wraparound purposes, it might also decide to analyze the table even if
> autovacuum is disabled for it.  AFAICT this is accidental, but since it's
> behaved this way since commit 48188e1621 (2006) [0], I am slightly worried
> that this bug may have become a feature.  In 0002, I separated this edge
> case in the code and added a comment, and I intend to start a new thread
> about removing it.

hmm yeah, I think this just needs to be documented clearly. I always
thought it was expected for auto-analyze to run in this case, and I don't
see why it shouldn't. If this needs to be clarified in docs, we should
do that in a separate discussion.

> * I removed the booleans in the view in favor of just noting that scores >=
> 1.0 means the table needs processing.  IMHO trying to distinguish
> needs_vacuum from do_vacuum is just going to confuse folks more than
> anything, and IIUC this information is redundant with "score >= 1.0",
> anyway.

That's fine by me.

> * I renamed the view to pg_autovacuum_scores.  While some of the
> information in the view depends on cumulative statistics, not all of it
> does, and what does is quite heavily modified from the original stats.  So,
> I didn't think the pg_stat_* prefix was appropriate, although I can see how
> reasonable people might disagree.

Initially I thought about moving this away from the cumulative stats section,
but this view does need to lookup relation stats and if relation stats
are reset,
the same rules will apply to this view.

Also not all views under "cumulative stats" are necessarily cumulative.
Some just show real-time data; pg_stat_activity, pg_stat_progress_*, etc.

This view does not have precedent in the type of work it does, but I do
really think it belongs under pg_stat_*, and not be too far away conceptually
from the vacuum stats in pg_stat_all_tables.

> * I considered whether to make the backing function per-table and
> ultimately decided against it.  The initialization logic is a bit
> expensive, and I assume most folks will be interested in the full picture
> of the current database.  Maybe we could add a per-table function down the
> road, but I don't see any strong need for that for now.

Yes, I did not proceed with this since the common use-case will be comparing
tables in a broader context. I don't see a string single table use-case at this
point.

Besides the above, I have one comment on 0005:

Where it says "indicate the table needs analyzing" or "needs processing"
or "needs vacuuming", we should instead say "may need". Since the
actually processing depends on the thresholds or force vacuum conditions,
but no need to go into that level of detail in the row descriptions. That is
all explained in the existing autovacuum prioritization docs.

--
Sami Imseih
Amazon Web Services (AWS)





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-04 02:40                                   ` Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-04 02:40 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

I committed 0001-0003.  Here is a new version of the last two patches.
Some notes:

* Instead of renaming recheck_relation_needs_vacanalyze(), I followed
Álvaro's suggestion to remove that function and instead fetch the stats
within relation_needs_vacanalyze() itself.

* Per your feedback, I renamed the view back to pg_stat_autovacuum_scores.

* Instead of limiting the view to pg_read_all_stats, I've left it
accessible to all users.  AFAICT there's nothing sensitive here.

* I reworked the column descriptions a bit to make it clear that values >=
1.0 mean autovacuum will process it (except if autovacuum is disabled).

WDYT?

-- 
nathan


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-04 13:25                                     ` Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-04 13:25 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> WDYT?

"Scores greater than or equal to <literal>1.0</literal>" in the comments
of each field are misleading. This conflates scoring with vacuum/analyze
eligibility and it's possible with a autovacuum_*_weight < 1.0 to trigger an
autovacuum/analyze.

I suggest the attached changes on top of v9.

--
Sami


Attachments:

  [application/octet-stream] 0001-sami_doc_fix.patch (3.9K, 2-0001-sami_doc_fix.patch)
  download | inline diff:
From 0fe7be2e0c4d970d0ec21af007d600c5bb94d904 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Sat, 4 Apr 2026 13:08:04 +0000
Subject: [PATCH 1/1] sami_doc_fix

---
 doc/src/sgml/monitoring.sgml | 28 +++++++++-------------------
 1 file changed, 9 insertions(+), 19 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 34e3051c400..47ed7768345 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4522,7 +4522,9 @@ description | Waiting for a newly initialized WAL file to reach durable storage
   <para>
    The <structname>pg_stat_autovacuum_scores</structname> view will contain one
    row for each table in the current database (including TOAST tables), showing
-   the current autovacuum scores for that specific table.  See
+   the current autovacuum scores for that specific table. Tables eligible for
+   autovacuum are prioritized for processing based on their score, with higher
+   scores indicating higher priority.  See
    <xref linkend="autovacuum-priority"/> for more information.
   </para>
 
@@ -4574,11 +4576,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para>
       <para>
        Maximum value of all component scores.  This is the value that
-       autovacuum uses to sort the list of tables to process.  Scores greater
-       than or equal to <literal>1.0</literal> indicate the table will be
-       processed (unless autovacuum is disabled and neither
-       <literal>xid_score</literal> nor <literal>mxid_score</literal> are
-       greater than or equal to <literal>1.0</literal>).
+       autovacuum uses to sort the list of tables to process.
       </para></entry>
      </row>
 
@@ -4587,8 +4585,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <structfield>xid_score</structfield> <type>double precision</type>
       </para>
       <para>
-       Transaction ID age component score.  Scores greater than or equal to
-       <literal>1.0</literal> indicate the table will be vacuumed.
+       Transaction ID age component score.
       </para></entry>
      </row>
 
@@ -4597,8 +4594,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <structfield>mxid_score</structfield> <type>double precision</type>
       </para>
       <para>
-       Multixact ID age component score.  Scores greater than or equal to
-       <literal>1.0</literal> indicate the table will be vacuumed.
+       Multixact ID age component score.
       </para></entry>
      </row>
 
@@ -4607,9 +4603,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <structfield>vacuum_score</structfield> <type>double precision</type>
       </para>
       <para>
-       Vacuum component score.  Scores greater than or equal to
-       <literal>1.0</literal> indicate the table will be vacuumed (unless
-       autovacuum is disabled).
+       Vacuum component score.
       </para></entry>
      </row>
 
@@ -4618,9 +4612,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <structfield>vacuum_insert_score</structfield> <type>double precision</type>
       </para>
       <para>
-       Vacuum insert component score.  Scores greater than or equal to
-       <literal>1.0</literal> indicate the table will be vacuumed (unless
-       autovacuum is disabled).
+       Vacuum insert component score.
       </para></entry>
      </row>
 
@@ -4629,9 +4621,7 @@ description | Waiting for a newly initialized WAL file to reach durable storage
        <structfield>analyze_score</structfield> <type>double precision</type>
       </para>
       <para>
-       Analyze component score.  Scores greater than or equal to
-       <literal>1.0</literal> indicate the table will be analyzed (unless
-       autovacuum is disabled).
+       Analyze component score.
       </para></entry>
      </row>
     </tbody>
-- 
2.50.1



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-04 14:33                                       ` Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-04 14:33 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

On Sat, Apr 04, 2026 at 08:25:26AM -0500, Sami Imseih wrote:
> "Scores greater than or equal to <literal>1.0</literal>" in the comments
> of each field are misleading. This conflates scoring with vacuum/analyze
> eligibility and it's possible with a autovacuum_*_weight < 1.0 to trigger an
> autovacuum/analyze.

Ah, that's unfortunate.  I think it'd be good to give folks some idea of
what autovacuum will actually process.  I wonder if we could adjust the
documentation accordingly.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-04 15:32                                         ` Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-04 15:32 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> On Sat, Apr 04, 2026 at 08:25:26AM -0500, Sami Imseih wrote:
> > "Scores greater than or equal to <literal>1.0</literal>" in the comments
> > of each field are misleading. This conflates scoring with vacuum/analyze
> > eligibility and it's possible with a autovacuum_*_weight < 1.0 to trigger an
> > autovacuum/analyze.
>
> Ah, that's unfortunate.  I think it'd be good to give folks some idea of
> what autovacuum will actually process.  I wonder if we could adjust the
> documentation accordingly.

That's why I thought having the bool fields made sense in the earlier
versions of the view. Since autovacuum is dealing with 2 concepts:

eligibility: is av enabled and is the table meeting thresholds
score: The priority of how the eligible tables will be processed.

So, while this could be explained in docs, I think it's better we report
these fields.

We might as well just call the view pg_stat_autovacuum in that case.

What do you think?

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-04 16:33                                           ` Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-04 16:33 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

On Sat, Apr 04, 2026 at 10:32:07AM -0500, Sami Imseih wrote:
>> On Sat, Apr 04, 2026 at 08:25:26AM -0500, Sami Imseih wrote:
>> > "Scores greater than or equal to <literal>1.0</literal>" in the comments
>> > of each field are misleading. This conflates scoring with vacuum/analyze
>> > eligibility and it's possible with a autovacuum_*_weight < 1.0 to trigger an
>> > autovacuum/analyze.
>>
>> Ah, that's unfortunate.  I think it'd be good to give folks some idea of
>> what autovacuum will actually process.  I wonder if we could adjust the
>> documentation accordingly.
> 
> That's why I thought having the bool fields made sense in the earlier
> versions of the view. Since autovacuum is dealing with 2 concepts:
> 
> eligibility: is av enabled and is the table meeting thresholds
> score: The priority of how the eligible tables will be processed.
> 
> So, while this could be explained in docs, I think it's better we report
> these fields.

I understand your position, but I still worry about potential confusion if
when dovacuum and needs_vacuum differ.  And I don't know how much we really
ought to be tailoring this stuff to clusters where autovacuum is disabled
or where the scores are being adjusted.  Also, I think we ought to go into
more detail in the documentation, anyway.  So my instinct was to do
something more like the attached.

If we did report booleans, I would probably argue for just reporting
dovacuum and doanalyze and calling out the criteria for why they may be
false even when it looks like the table needs processing.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-04 17:48                                             ` Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-04 17:48 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> > eligibility: is av enabled and is the table meeting thresholds
> > score: The priority of how the eligible tables will be processed.
> >
> > So, while this could be explained in docs, I think it's better we report
> > these fields.
>
> I understand your position, but I still worry about potential confusion if
> when dovacuum and needs_vacuum differ.  And I don't know how much we really
> ought to be tailoring this stuff to clusters where autovacuum is disabled
> or where the scores are being adjusted.  Also, I think we ought to go into
> more detail in the documentation, anyway.

After looking at it a bit more, I don't think we can avoid putting
the needs_analyze and needs_vacuum fields, because without them
there will still be a gap in understanding of if a table is eligible for
autovacuum. A score on its own will not draw the full picture. For example,
if I tune the _weight settings, I want to know if this table now becomes
a candidate for vacuum/analyze, irrespective if autovacuum will actually
process it due to some bad configuration. I can see someone seaching
this view by just the bool fields to see the eligibile tables also without
caring about the score.

> If we did report booleans, I would probably argue for just reporting
> dovacuum and doanalyze and calling out the criteria for why they may be
> false even when it looks like the table needs processing.

Yes, we only require a needs_analyze and needs_vacuum. The latter
can be set to true due to thresholds or wraparound. But, I don't think we
should rely on the dovacuum or doanalyze, instead we can just have a flag
in AutoVacuumScores->needs and track what is needed. This will separate
the autovacuum processing from the reporting.

Here is what I am thinking we should do.

v11-0001 is same as v10-0001
v11-0002 introduces the additional bit field for reporting. This sets the
need for vacuum regardless of av_enabled.
v11-0003 the view with slightly more detailed documentation.

--
Sami


Attachments:

  [application/octet-stream] v11-0002-Add-vacuum-need-flag-to-AutoVacuumScores.patch (2.5K, 2-v11-0002-Add-vacuum-need-flag-to-AutoVacuumScores.patch)
  download | inline diff:
From 9b14c37136fdd3183e6a21a9a02f82c70c4b1600 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Sat, 4 Apr 2026 16:46:24 +0000
Subject: [PATCH v11 2/3] Add vacuum need flag to AutoVacuumScores

---
 src/backend/postmaster/autovacuum.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1be1ba8a25f..1bcd4ac4a17 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -329,8 +329,13 @@ typedef struct
 	double		vac;			/* vacuum component */
 	double		vac_ins;		/* vacuum insert component */
 	double		anl;			/* analyze component */
+	int			needs;			/* What type of vacuuming is needed */
 } AutoVacuumScores;
 
+/* The flags for AutoVacuumScores->needs */
+#define NEEDS_VACUUM 0x1
+#define NEEDS_ANALYZE 0x2
+
 /*
  * This struct is used to track and sort the list of tables to process.
  */
@@ -3051,6 +3056,8 @@ relation_needs_vacanalyze(Oid relid,
 	PgStat_StatTabEntry *tabentry;
 	bool		force_vacuum;
 	bool		av_enabled;
+	bool		needs_vacuum;
+	bool		needs_vacuum_ins;
 
 	/* constants from reloptions or GUC variables */
 	int			vac_base_thresh,
@@ -3258,15 +3265,20 @@ relation_needs_vacanalyze(Oid relid,
 	scores->vac = (double) vactuples / Max(vacthresh, 1);
 	scores->vac *= autovacuum_vacuum_score_weight;
 	scores->max = Max(scores->max, scores->vac);
-	if (av_enabled && vactuples > vacthresh)
-		*dovacuum = true;
+	needs_vacuum = (vactuples > vacthresh);
 
 	if (vac_ins_base_thresh >= 0)
 	{
 		scores->vac_ins = (double) instuples / Max(vacinsthresh, 1);
 		scores->vac_ins *= autovacuum_vacuum_insert_score_weight;
 		scores->max = Max(scores->max, scores->vac_ins);
-		if (av_enabled && instuples > vacinsthresh)
+	}
+	needs_vacuum_ins = (vac_ins_base_thresh >= 0 && instuples > vacinsthresh);
+
+	if (needs_vacuum || needs_vacuum_ins)
+	{
+		scores->needs |= NEEDS_VACUUM;
+		if (av_enabled)
 			*dovacuum = true;
 	}
 
@@ -3280,8 +3292,12 @@ relation_needs_vacanalyze(Oid relid,
 		scores->anl = (double) anltuples / Max(anlthresh, 1);
 		scores->anl *= autovacuum_analyze_score_weight;
 		scores->max = Max(scores->max, scores->anl);
-		if (av_enabled && anltuples > anlthresh)
-			*doanalyze = true;
+		if (anltuples > anlthresh)
+		{
+			scores->needs |= NEEDS_ANALYZE;
+			if (av_enabled)
+				*doanalyze = true;
+		}
 
 		/*
 		 * For historical reasons, we analyze even when autovacuum is disabled
-- 
2.50.1



  [application/octet-stream] v11-0003-add-pg_stat_autovacuum_scores-system-view.patch (13.0K, 3-v11-0003-add-pg_stat_autovacuum_scores-system-view.patch)
  download | inline diff:
From 8f4544c240f5d7d2c59201cd69685d6278138905 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <[email protected]>
Date: Thu, 2 Apr 2026 15:47:19 -0500
Subject: [PATCH v11 3/3] add pg_stat_autovacuum_scores system view

---
 doc/src/sgml/maintenance.sgml        |   7 ++
 doc/src/sgml/monitoring.sgml         | 151 +++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |  17 +++
 src/backend/postmaster/autovacuum.c  |  72 +++++++++++++
 src/include/catalog/pg_proc.dat      |   7 ++
 src/test/regress/expected/rules.out  |  14 +++
 6 files changed, 268 insertions(+)

diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 0d2a28207ed..bf4b6f4c309 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -1164,6 +1164,13 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
      <literal>2.0</literal> effectively doubles the
      <emphasis>analyze</emphasis> component score.
     </para>
+
+    <para>
+     The <link linkend="monitoring-pg-stat-autovacuum-scores-view">
+     <structname>pg_stat_autovacuum_scores</structname></link>
+     view shows whether vacuum or analyze thresholds have been exceeded
+     and the current autovacuum scores for all tables in the current database.
+    </para>
    </sect3>
   </sect2>
  </sect1>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 312374da5e0..bd2c4d43731 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -596,6 +596,16 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       user tables are shown.</entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_autovacuum_scores</structname><indexterm><primary>pg_stat_autovacuum_scores</primary></indexterm></entry>
+      <entry>
+       One row for each table in the current database, showing the current
+       autovacuum scores for that specific table.  See
+       <link linkend="monitoring-pg-stat-autovacuum-scores-view">
+       <structname>pg_stat_autovacuum_scores</structname></link> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_all_indexes</structname><indexterm><primary>pg_stat_all_indexes</primary></indexterm></entry>
       <entry>
@@ -4502,6 +4512,147 @@ description | Waiting for a newly initialized WAL file to reach durable storage
 
  </sect2>
 
+ <sect2 id="monitoring-pg-stat-autovacuum-scores-view">
+  <title><structname>pg_stat_autovacuum_scores</structname></title>
+
+  <indexterm>
+   <primary>pg_stat_autovacuum_scores</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_stat_autovacuum_scores</structname> view will contain one
+   row for each table in the current database (including TOAST tables), showing
+   whether vacuum or analyze thresholds have been exceeded and the current
+   autovacuum scores for that specific table.  Tables without access
+   statistics will not report a score, except for a wraparound score.
+   See <xref linkend="autovacuum-priority"/> for more information.
+  </para>
+
+  <table id="pg-stat-autovacuum-scores-view" xreflabel="pg_stat_autovacuum_scores">
+   <title><structname>pg_stat_autovacuum_scores</structname> View</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relid</structfield> <type>oid</type>
+      </para>
+      <para>
+       Oid of the table.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>schemaname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the schema that the table is in.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>relname</structfield> <type>name</type>
+      </para>
+      <para>
+       Name of the table.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_vacuum</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table needs to be vacuumed, based on whether the vacuum
+       threshold has been exceeded or the table is at risk of wraparound.
+       This does not imply that autovacuum will actually process the table,
+       e.g., if autovacuum is disabled for the table.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>needs_analyze</structfield> <type>boolean</type>
+      </para>
+      <para>
+       True if the table needs to be analyzed, based on whether the analyze
+       threshold has been exceeded.
+       This does not imply that autovacuum will actually process the table,
+       e.g., if autovacuum is disabled for the table.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Maximum value of all component scores.  This is the value that
+       autovacuum uses to sort the list of tables to process.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>xid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Transaction ID age component score.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>mxid_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Multixact ID age component score.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Vacuum component score.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>vacuum_insert_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Vacuum insert component score.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>analyze_score</structfield> <type>double precision</type>
+      </para>
+      <para>
+       Analyze component score.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect2>
+
  <sect2 id="monitoring-pg-stat-all-indexes-view">
   <title><structname>pg_stat_all_indexes</structname></title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index eba25aa3e4d..11ce94eb9c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -795,6 +795,23 @@ CREATE VIEW pg_stat_xact_user_tables AS
     WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND
           schemaname !~ '^pg_toast';
 
+CREATE VIEW pg_stat_autovacuum_scores AS
+    SELECT
+        s.oid AS relid,
+        n.nspname AS schemaname,
+        c.relname AS relname,
+        s.needs_vacuum,
+        s.needs_analyze,
+        s.score,
+        s.xid_score,
+        s.mxid_score,
+        s.vacuum_score,
+        s.vacuum_insert_score,
+        s.analyze_score
+    FROM pg_stat_get_autovacuum_scores() s
+    JOIN pg_class c on c.oid = s.oid
+    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace;
+
 CREATE VIEW pg_statio_all_tables AS
     SELECT
             C.oid AS relid,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1bcd4ac4a17..5cd6b4e0bb2 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -80,6 +80,7 @@
 #include "catalog/pg_namespace.h"
 #include "commands/vacuum.h"
 #include "common/int.h"
+#include "funcapi.h"
 #include "lib/ilist.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -111,6 +112,7 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
+#include "utils/tuplestore.h"
 #include "utils/wait_event.h"
 
 
@@ -3639,3 +3641,73 @@ check_av_worker_gucs(void)
 				 errdetail("The server will only start up to \"autovacuum_worker_slots\" (%d) autovacuum workers at a given time.",
 						   autovacuum_worker_slots)));
 }
+
+/*
+ * pg_stat_get_autovacuum_scores
+ *
+ * Returns current autovacuum scores for all relevant tables in the current
+ * database.
+ */
+Datum
+pg_stat_get_autovacuum_scores(PG_FUNCTION_ARGS)
+{
+	int			effective_multixact_freeze_max_age;
+	Relation	rel;
+	TableScanDesc scan;
+	HeapTuple	tup;
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	/* some prerequisite initialization */
+	effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+	recentXid = ReadNextTransactionId();
+	recentMulti = ReadNextMultiXactId();
+
+	/* scan pg_class */
+	rel = table_open(RelationRelationId, AccessShareLock);
+	scan = table_beginscan_catalog(rel, 0, NULL);
+	while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class form = (Form_pg_class) GETSTRUCT(tup);
+		AutoVacOpts *avopts;
+		bool		dovacuum;
+		bool		doanalyze;
+		bool		wraparound;
+		AutoVacuumScores scores;
+		Datum		vals[9];
+		bool		nulls[9] = {false};
+
+		/* skip ineligible entries */
+		if (form->relkind != RELKIND_RELATION &&
+			form->relkind != RELKIND_MATVIEW &&
+			form->relkind != RELKIND_TOASTVALUE)
+			continue;
+		if (form->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+
+		avopts = extract_autovac_opts(tup, RelationGetDescr(rel));
+		relation_needs_vacanalyze(form->oid, avopts, form,
+								  effective_multixact_freeze_max_age, 0,
+								  &dovacuum, &doanalyze, &wraparound,
+								  &scores);
+		if (avopts)
+			pfree(avopts);
+
+		vals[0] = ObjectIdGetDatum(form->oid);
+		vals[1] = BoolGetDatum(wraparound || (scores.needs & NEEDS_VACUUM));
+		vals[2] = BoolGetDatum(scores.needs & NEEDS_ANALYZE);
+		vals[3] = Float8GetDatum(scores.max);
+		vals[4] = Float8GetDatum(scores.xid);
+		vals[5] = Float8GetDatum(scores.mxid);
+		vals[6] = Float8GetDatum(scores.vac);
+		vals[7] = Float8GetDatum(scores.vac_ins);
+		vals[8] = Float8GetDatum(scores.anl);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, vals, nulls);
+	}
+	table_endscan(scan);
+	table_close(rel, AccessShareLock);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index bd177aebfcb..f3dbf8e0887 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,13 @@
   proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8409', descr => 'autovacuum scores',
+  proname => 'pg_stat_get_autovacuum_scores', prorows => '100', proretset => 't',
+  provolatile => 'v', proparallel => 'u', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{oid,bool,bool,float8,float8,float8,float8,float8,float8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o}',
+  proargnames => '{oid,needs_vacuum,needs_analyze,score,xid_score,mxid_score,vacuum_score,vacuum_insert_score,analyze_score}',
+  prosrc => 'pg_stat_get_autovacuum_scores' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 81a73c426d2..a5ee2135cae 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1860,6 +1860,20 @@ pg_stat_archiver| SELECT archived_count,
     last_failed_time,
     stats_reset
    FROM pg_stat_get_archiver() s(archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, stats_reset);
+pg_stat_autovacuum_scores| SELECT s.oid AS relid,
+    n.nspname AS schemaname,
+    c.relname,
+    s.needs_vacuum,
+    s.needs_analyze,
+    s.score,
+    s.xid_score,
+    s.mxid_score,
+    s.vacuum_score,
+    s.vacuum_insert_score,
+    s.analyze_score
+   FROM ((pg_stat_get_autovacuum_scores() s(oid, needs_vacuum, needs_analyze, score, xid_score, mxid_score, vacuum_score, vacuum_insert_score, analyze_score)
+     JOIN pg_class c ON ((c.oid = s.oid)))
+     LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)));
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
-- 
2.50.1



  [application/octet-stream] v11-0001-refactor-autovacuum-subroutine-in-preparation-fo.patch (6.1K, 4-v11-0001-refactor-autovacuum-subroutine-in-preparation-fo.patch)
  download | inline diff:
From 41ac6799d0cef1868d2684a021b5c40ad2702353 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <[email protected]>
Date: Thu, 2 Apr 2026 14:17:11 -0500
Subject: [PATCH v11 1/3] refactor autovacuum subroutine in preparation for
 system view

---
 src/backend/postmaster/autovacuum.c | 73 ++++++-----------------------
 1 file changed, 15 insertions(+), 58 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 8400e6722cc..1be1ba8a25f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -370,13 +370,8 @@ static void FreeWorkerInfo(int code, Datum arg);
 static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 											TupleDesc pg_class_desc,
 											int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
-											  Form_pg_class classForm,
-											  int effective_multixact_freeze_max_age,
-											  bool *dovacuum, bool *doanalyze, bool *wraparound);
 static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
 									  Form_pg_class classForm,
-									  PgStat_StatTabEntry *tabentry,
 									  int effective_multixact_freeze_max_age,
 									  int elevel,
 									  bool *dovacuum, bool *doanalyze, bool *wraparound,
@@ -2029,7 +2024,6 @@ do_autovacuum(void)
 	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
 	{
 		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
-		PgStat_StatTabEntry *tabentry;
 		AutoVacOpts *relopts;
 		Oid			relid;
 		bool		dovacuum;
@@ -2070,11 +2064,9 @@ do_autovacuum(void)
 
 		/* Fetch reloptions and the pgstat entry for this table */
 		relopts = extract_autovac_opts(tuple, pg_class_desc);
-		tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-												  relid);
 
 		/* Check if it needs vacuum or analyze */
-		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
+		relation_needs_vacanalyze(relid, relopts, classForm,
 								  effective_multixact_freeze_max_age,
 								  DEBUG3,
 								  &dovacuum, &doanalyze, &wraparound,
@@ -2121,8 +2113,6 @@ do_autovacuum(void)
 		/* Release stuff to avoid per-relation leakage */
 		if (relopts)
 			pfree(relopts);
-		if (tabentry)
-			pfree(tabentry);
 	}
 
 	table_endscan(relScan);
@@ -2137,7 +2127,6 @@ do_autovacuum(void)
 	while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
 	{
 		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
-		PgStat_StatTabEntry *tabentry;
 		Oid			relid;
 		AutoVacOpts *relopts;
 		bool		free_relopts = false;
@@ -2171,11 +2160,7 @@ do_autovacuum(void)
 				relopts = &hentry->ar_reloptions;
 		}
 
-		/* Fetch the pgstat entry for this table */
-		tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-												  relid);
-
-		relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
+		relation_needs_vacanalyze(relid, relopts, classForm,
 								  effective_multixact_freeze_max_age,
 								  DEBUG3,
 								  &dovacuum, &doanalyze, &wraparound,
@@ -2194,8 +2179,6 @@ do_autovacuum(void)
 		/* Release stuff to avoid leakage */
 		if (free_relopts)
 			pfree(relopts);
-		if (tabentry)
-			pfree(tabentry);
 	}
 
 	table_endscan(relScan);
@@ -2834,6 +2817,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	bool		wraparound;
 	AutoVacOpts *avopts;
 	bool		free_avopts = false;
+	AutoVacuumScores scores;
 
 	/* fetch the relation's relcache entry */
 	classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2859,9 +2843,11 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			avopts = &hentry->ar_reloptions;
 	}
 
-	recheck_relation_needs_vacanalyze(relid, avopts, classForm,
-									  effective_multixact_freeze_max_age,
-									  &dovacuum, &doanalyze, &wraparound);
+	relation_needs_vacanalyze(relid, avopts, classForm,
+							  effective_multixact_freeze_max_age,
+							  DEBUG3,
+							  &dovacuum, &doanalyze, &wraparound,
+							  &scores);
 
 	/* OK, it needs something done */
 	if (doanalyze || dovacuum)
@@ -2970,41 +2956,6 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	return tab;
 }
 
-/*
- * recheck_relation_needs_vacanalyze
- *
- * Subroutine for table_recheck_autovac.
- *
- * Fetch the pgstat of a relation and recheck whether a relation
- * needs to be vacuumed or analyzed.
- */
-static void
-recheck_relation_needs_vacanalyze(Oid relid,
-								  AutoVacOpts *avopts,
-								  Form_pg_class classForm,
-								  int effective_multixact_freeze_max_age,
-								  bool *dovacuum,
-								  bool *doanalyze,
-								  bool *wraparound)
-{
-	PgStat_StatTabEntry *tabentry;
-	AutoVacuumScores scores;
-
-	/* fetch the pgstat table entry */
-	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
-
-	relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
-							  effective_multixact_freeze_max_age,
-							  DEBUG3,
-							  dovacuum, doanalyze, wraparound,
-							  &scores);
-
-	/* Release tabentry to avoid leakage */
-	if (tabentry)
-		pfree(tabentry);
-}
-
 /*
  * relation_needs_vacanalyze
  *
@@ -3089,7 +3040,6 @@ static void
 relation_needs_vacanalyze(Oid relid,
 						  AutoVacOpts *relopts,
 						  Form_pg_class classForm,
-						  PgStat_StatTabEntry *tabentry,
 						  int effective_multixact_freeze_max_age,
 						  int elevel,
  /* output params below */
@@ -3098,6 +3048,7 @@ relation_needs_vacanalyze(Oid relid,
 						  bool *wraparound,
 						  AutoVacuumScores *scores)
 {
+	PgStat_StatTabEntry *tabentry;
 	bool		force_vacuum;
 	bool		av_enabled;
 
@@ -3265,6 +3216,8 @@ relation_needs_vacanalyze(Oid relid,
 	 * vacuuming only, so don't vacuum (or analyze) anything that's not being
 	 * forced.
 	 */
+	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
+											  relid);
 	if (!tabentry)
 		return;
 
@@ -3353,6 +3306,10 @@ relation_needs_vacanalyze(Oid relid,
 			 vactuples, vacthresh, scores->vac,
 			 anltuples, anlthresh, scores->anl,
 			 scores->xid, scores->mxid);
+
+	/* Release tabentry to avoid leakage */
+	if (tabentry)
+		pfree(tabentry);
 }
 
 /*
-- 
2.50.1



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-04 19:10                                               ` Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-04 19:10 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

On Sat, Apr 04, 2026 at 12:48:28PM -0500, Sami Imseih wrote:
>> If we did report booleans, I would probably argue for just reporting
>> dovacuum and doanalyze and calling out the criteria for why they may be
>> false even when it looks like the table needs processing.
> 
> Yes, we only require a needs_analyze and needs_vacuum. The latter
> can be set to true due to thresholds or wraparound. But, I don't think we
> should rely on the dovacuum or doanalyze, instead we can just have a flag
> in AutoVacuumScores->needs and track what is needed. This will separate
> the autovacuum processing from the reporting.

Sorry for going in circles about this, but I'm not seeing why we wouldn't
just return the booleans that relation_needs_vacanalyze() already returns.
I think the question people will have is "what will autovacuum process and
in what order?", and if we aren't giving them the exact same information
that autovacuum is using to make its decisions, then I'm not sure what is
the point.  It's true that someone might disable autovacuum for a table and
that it would otherwise be processed, but so be it.

Concretely, like the attached 0003.  IMHO this feels much more natural than
giving folks booleans that usually represents dovacuum/doanalyze but that
don't in certain cases.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-04 22:35                                                 ` Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-04 22:35 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> >> If we did report booleans, I would probably argue for just reporting
> >> dovacuum and doanalyze and calling out the criteria for why they may be
> >> false even when it looks like the table needs processing.
> >
> > Yes, we only require a needs_analyze and needs_vacuum. The latter
> > can be set to true due to thresholds or wraparound. But, I don't think we
> > should rely on the dovacuum or doanalyze, instead we can just have a flag
> > in AutoVacuumScores->needs and track what is needed. This will separate
> > the autovacuum processing from the reporting.
>
> Sorry for going in circles about this, but I'm not seeing why we wouldn't
> just return the booleans that relation_needs_vacanalyze() already returns.
> I think the question people will have is "what will autovacuum process and
> in what order?", and if we aren't giving them the exact same information
> that autovacuum is using to make its decisions, then I'm not sure what is
> the point.  It's true that someone might disable autovacuum for a table and
> that it would otherwise be processed, but so be it.

Maybe there’s no need to worry too much about the autovacuum disabled
case or track_counts being disabled when querying the view. Both are
edge cases, and it seemed fairly trivial to compensate for this with what
I had attached earlier. Anyhow, I will not push this point further. I am ok
with proceeding with what you have in v12. The patches overall LGTM.

Thanks!

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-06 21:58                                                   ` Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-06 21:58 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

Committed after some more editorialization.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 17:00                                                     ` Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 18:14                                                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 2 replies; 60+ messages in thread

From: Alexander Lakhin @ 2026-04-08 17:00 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; Sami Imseih <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

Hello Nathan,

07.04.2026 00:58, Nathan Bossart wrote:
> Committed after some more editorialization.

Please look at a new anomaly, I and SQLsmith have discovered:
SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
     (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
ERROR:  detected double pfree in PgStat Snapshot 0x5f6fa4d95d50

Best regards,
Alexander





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
@ 2026-04-08 17:17                                                       ` Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Tom Lane @ 2026-04-08 17:17 UTC (permalink / raw)
  To: Alexander Lakhin <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

Alexander Lakhin <[email protected]> writes:
> Please look at a new anomaly, I and SQLsmith have discovered:
> SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
>      (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
> ERROR:  detected double pfree in PgStat Snapshot 0x5f6fa4d95d50

Good catch, but you're not the first:

https://www.postgresql.org/message-id/CAHewXNkJKdwb3D5OnksrdOqzqUnXUEMpDam1TPW0vfUkW%3D7jUw%40mail.g...

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
@ 2026-04-08 17:56                                                         ` Nathan Bossart <[email protected]>
  2026-04-08 18:18                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  0 siblings, 2 replies; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 17:56 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 01:17:45PM -0400, Tom Lane wrote:
> Alexander Lakhin <[email protected]> writes:
>> Please look at a new anomaly, I and SQLsmith have discovered:
>> SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
>>      (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
>> ERROR:  detected double pfree in PgStat Snapshot 0x5f6fa4d95d50
> 
> Good catch, but you're not the first:
> 
> https://www.postgresql.org/message-id/CAHewXNkJKdwb3D5OnksrdOqzqUnXUEMpDam1TPW0vfUkW%3D7jUw%40mail.g...

Hm.  I can't get excited about checking pgstat_fetch_consistency (as
proposed in that other report), but I see that commit 02502c1bca added the
freeing behavior in question.  I wonder if it makes sense to just skip
freeing when relation_needs_vacanalyze() is called from the view, i.e., not
an autovacuum worker.  On the other hand, maybe we shouldn't be caching
entries for a view like this that looks through all tables in the
database...

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 18:18                                                           ` Nathan Bossart <[email protected]>
  2026-04-08 18:28                                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 18:18 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 12:56:09PM -0500, Nathan Bossart wrote:
> Hm.  I can't get excited about checking pgstat_fetch_consistency (as
> proposed in that other report), but I see that commit 02502c1bca added the
> freeing behavior in question.  I wonder if it makes sense to just skip
> freeing when relation_needs_vacanalyze() is called from the view, i.e., not
> an autovacuum worker.  On the other hand, maybe we shouldn't be caching
> entries for a view like this that looks through all tables in the
> database...

Concretely, this is what I'm thinking:

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index bd626a16363..6d4a34257fb 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -3327,7 +3327,15 @@ relation_needs_vacanalyze(Oid relid,
              anltuples, anlthresh, scores->anl,
              scores->xid, scores->mxid);

-    pfree(tabentry);
+    /*
+     * Avoid leaking pgstat entries until the end of autovacuum.  Elsewhere,
+     * we let the commit/abort machinery take care of freeing it.  While
+     * autovacuum workers set stats_fetch_consistency to "none", it might be
+     * set to a different value in other processes, so we can't safely free it
+     * here for them.
+     */
+    if (AmAutoVacuumWorkerProcess())
+        pfree(tabentry);
 }

 /*

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:18                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 18:28                                                             ` Sami Imseih <[email protected]>
  2026-04-08 18:37                                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-08 18:28 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

> diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
> index bd626a16363..6d4a34257fb 100644
> --- a/src/backend/postmaster/autovacuum.c
> +++ b/src/backend/postmaster/autovacuum.c
> @@ -3327,7 +3327,15 @@ relation_needs_vacanalyze(Oid relid,
>               anltuples, anlthresh, scores->anl,
>               scores->xid, scores->mxid);
>
> -    pfree(tabentry);
> +    /*
> +     * Avoid leaking pgstat entries until the end of autovacuum.  Elsewhere,
> +     * we let the commit/abort machinery take care of freeing it.  While
> +     * autovacuum workers set stats_fetch_consistency to "none", it might be
> +     * set to a different value in other processes, so we can't safely free it
> +     * here for them.
> +     */
> +    if (AmAutoVacuumWorkerProcess())
> +        pfree(tabentry);
>  }

This works too, but v1-0001 is more generalized and we don't have to
care about who the caller is when deciding to free or not.

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:18                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:28                                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-08 18:37                                                               ` Nathan Bossart <[email protected]>
  2026-04-08 18:44                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 18:37 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 01:28:06PM -0500, Sami Imseih wrote:
>> +    if (AmAutoVacuumWorkerProcess())
>> +        pfree(tabentry);
>>  }
> 
> This works too, but v1-0001 is more generalized and we don't have to
> care about who the caller is when deciding to free or not.

Well, I think if we were building this view from scratch without any
knowledge of autovacuum, we probably wouldnt bother manually freeing the
stats entries based on the value of the GUC.  We'd probably just let the
commit/abort code take care of it, which AFAICT is what we do elsewhere.
Manually freeing the entries is also risky, which commit 02502c1bca touches
on:

    Note: pfree'ing the PgStat_StatTabEntry structs here seems a bit
    risky, because pgstat_fetch_stat_tabentry_ext does not guarantee
    anything about whether its result is long-lived.  It appears okay
    so long as autovacuum forces PGSTAT_FETCH_CONSISTENCY_NONE, but
    I think that API could use a re-think.

This seems very much to be an autovacuum-specific hack that we shouldn't be
propagating elsewhere.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:18                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:28                                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 18:37                                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 18:44                                                                 ` Andres Freund <[email protected]>
  0 siblings, 0 replies; 60+ messages in thread

From: Andres Freund @ 2026-04-08 18:44 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Sami Imseih <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Hi,

On 2026-04-08 13:37:31 -0500, Nathan Bossart wrote:
> On Wed, Apr 08, 2026 at 01:28:06PM -0500, Sami Imseih wrote:
> >> +    if (AmAutoVacuumWorkerProcess())
> >> +        pfree(tabentry);
> >>  }
> > 
> > This works too, but v1-0001 is more generalized and we don't have to
> > care about who the caller is when deciding to free or not.
> 
> Well, I think if we were building this view from scratch without any
> knowledge of autovacuum, we probably wouldnt bother manually freeing the
> stats entries based on the value of the GUC.  We'd probably just let the
> commit/abort code take care of it, which AFAICT is what we do elsewhere.
> Manually freeing the entries is also risky, which commit 02502c1bca touches
> on:
> 
>     Note: pfree'ing the PgStat_StatTabEntry structs here seems a bit
>     risky, because pgstat_fetch_stat_tabentry_ext does not guarantee
>     anything about whether its result is long-lived.  It appears okay
>     so long as autovacuum forces PGSTAT_FETCH_CONSISTENCY_NONE, but
>     I think that API could use a re-think.
> 
> This seems very much to be an autovacuum-specific hack that we shouldn't be
> propagating elsewhere.

Agreed.  There aren't that many callers of pgstat_fetch_entry (even if you
count indirect ones), I think the best path might be to add a bool *may_free
argument where necessary and just deal with the size of change that brings
with it.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 18:53                                                           ` Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Tom Lane @ 2026-04-08 18:53 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Nathan Bossart <[email protected]> writes:
> Hm.  I can't get excited about checking pgstat_fetch_consistency (as
> proposed in that other report), but I see that commit 02502c1bca added the
> freeing behavior in question.  I wonder if it makes sense to just skip
> freeing when relation_needs_vacanalyze() is called from the view, i.e., not
> an autovacuum worker.  On the other hand, maybe we shouldn't be caching
> entries for a view like this that looks through all tables in the
> database...

<carp>
Oh.  I'm not happy that any part of autovacuum.c is now reachable from
SQL: that's the sort of modularity violation that will bite us on the
ass (indeed just did).  Aside from this problem, the elog's that
relation_needs_vacanalyze emits seem 100% inappropriate and misleading
when it's being called from the view.
</carp>

I think perhaps the right way forward is to rethink the API
guarantees for pgstat_fetch_stat_tabentry_ext, as I speculated
about in 02502c1bc:

    Note: pfree'ing the PgStat_StatTabEntry structs here seems a bit
    risky, because pgstat_fetch_stat_tabentry_ext does not guarantee
    anything about whether its result is long-lived.  It appears okay
    so long as autovacuum forces PGSTAT_FETCH_CONSISTENCY_NONE, but
    I think that API could use a re-think.

I didn't want to do any such thing in a bug fix that needed to be
back-patched, but I see no reason we couldn't redefine that API
for v19.  Plausible alternatives:

1. Always return a freshly palloc'd struct.  Potentially adds
cycles, adds risk of a leak if caller forgets to pfree.

2. Add a "bool *should_free" parameter, like we have in tuplestores
and some other places.  It's on the caller to pfree if should_free
gets set, but since we'd have to touch every caller, we'd not miss
any.

3. Add a "bool please_palloc" parameter, signaling the caller's
intent to pfree.  Probably has no advantage over #2 though.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
@ 2026-04-08 19:09                                                             ` Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 19:09 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 02:53:20PM -0400, Tom Lane wrote:
> Oh.  I'm not happy that any part of autovacuum.c is now reachable from
> SQL: that's the sort of modularity violation that will bite us on the
> ass (indeed just did).  Aside from this problem, the elog's that
> relation_needs_vacanalyze emits seem 100% inappropriate and misleading
> when it's being called from the view.

FWIW that elog() shouldn't be emitting anything from the view, unless
something is broken.

> I think perhaps the right way forward is to rethink the API
> guarantees for pgstat_fetch_stat_tabentry_ext, as I speculated
> about in 02502c1bc:
>
> [...]
> 
> 2. Add a "bool *should_free" parameter, like we have in tuplestores
> and some other places.  It's on the caller to pfree if should_free
> gets set, but since we'd have to touch every caller, we'd not miss
> any.

This sounds most similar to the "bool *may_free" idea that Andres just
posted.  IIUC the idea is that callers can free the result if they want,
but they aren't required to do so.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 19:21                                                               ` Tom Lane <[email protected]>
  2026-04-08 19:35                                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:19                                                                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 3 replies; 60+ messages in thread

From: Tom Lane @ 2026-04-08 19:21 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Nathan Bossart <[email protected]> writes:
> On Wed, Apr 08, 2026 at 02:53:20PM -0400, Tom Lane wrote:
>> ... Aside from this problem, the elog's that
>> relation_needs_vacanalyze emits seem 100% inappropriate and misleading
>> when it's being called from the view.

> FWIW that elog() shouldn't be emitting anything from the view, unless
> something is broken.

[ looks again... ]  Oh, you mean because pg_stat_get_autovacuum_scores
passes 0 for elevel instead of any of the valid elevel constants.
Can you say "undocumented cowboy hack"?  I'd be happier if we added
something like "#define LOG_NEVER 0  /* Never emit this message */"
to elog.h and used that.

>> 2. Add a "bool *should_free" parameter, like we have in tuplestores
>> and some other places.  It's on the caller to pfree if should_free
>> gets set, but since we'd have to touch every caller, we'd not miss
>> any.

> This sounds most similar to the "bool *may_free" idea that Andres just
> posted.  IIUC the idea is that callers can free the result if they want,
> but they aren't required to do so.

Hmm, yeah I suppose a caller that doesn't care about leakage could
skip the pfree.  But are there really any of those?  The complaint
that prompted 02502c1bc concerned databases with many many thousands
of relations.

I now realize that what you said upthread about caching the results
might be a bigger problem, ie if the pgstats code does retain all
these values then we'd have a memory bloat problem there.  Maybe
we need a more aggressive API change that includes a way to specify
"don't cache this result".

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
@ 2026-04-08 19:35                                                                 ` Sami Imseih <[email protected]>
  2 siblings, 0 replies; 60+ messages in thread

From: Sami Imseih @ 2026-04-08 19:35 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

> > This sounds most similar to the "bool *may_free" idea that Andres just
> > posted.  IIUC the idea is that callers can free the result if they want,
> > but they aren't required to do so.
>
> Hmm, yeah I suppose a caller that doesn't care about leakage could
> skip the pfree.  But are there really any of those?  The complaint
> that prompted 02502c1bc concerned databases with many many thousands
> of relations.
>
> I now realize that what you said upthread about caching the results
> might be a bigger problem, ie if the pgstats code does retain all
> these values then we'd have a memory bloat problem there.  Maybe
> we need a more aggressive API change that includes a way to specify
> "don't cache this result".

hmm, do you mean an API to override the pgstat_fetch_consistency GUC?

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
@ 2026-04-08 20:40                                                                 ` Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2 siblings, 1 reply; 60+ messages in thread

From: Andres Freund @ 2026-04-08 20:40 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Hi,

On 2026-04-08 15:21:38 -0400, Tom Lane wrote:
> Hmm, yeah I suppose a caller that doesn't care about leakage could
> skip the pfree.  But are there really any of those?  The complaint
> that prompted 02502c1bc concerned databases with many many thousands
> of relations.

Most of the calls come from SQL callable functions that just return a value to
the user. For those I don't think we gain anything by doing retail pfrees, if
anything it'd hurt performance.


> I now realize that what you said upthread about caching the results
> might be a bigger problem, ie if the pgstats code does retain all
> these values then we'd have a memory bloat problem there.  Maybe
> we need a more aggressive API change that includes a way to specify
> "don't cache this result".

I can't stop myself from pointing out that the whole reason this caching stuff
exists is because you insistet that we provide an equivalent for snapshotting
semantics of the pre-shmem stats system...

Note that the whole cached state does automatically get reset at the end of
the transaction (AtEOXact_PgStat()->pgstat_clear_snapshot()), just like it did
before the shmem stats stuff.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
@ 2026-04-08 21:23                                                                   ` Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 21:59                                                                     ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  0 siblings, 2 replies; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 21:23 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 04:40:03PM -0400, Andres Freund wrote:
> Note that the whole cached state does automatically get reset at the end of
> the transaction (AtEOXact_PgStat()->pgstat_clear_snapshot()), just like it did
> before the shmem stats stuff.

I see a lot of memory used for the pgStatEntryRefHash table, too (e.g., ~16
MB for 100K tables).  What's interesting is that I cannot reproduce similar
usage with views like pg_stat_all_tables.  If memory was not a concern, I
think the "bool *may_free" idea would be fine.  But assuming it is, we will
probably need to do something more creative.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 21:33                                                                     ` Sami Imseih <[email protected]>
  2026-04-08 21:48                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Sami Imseih @ 2026-04-08 21:33 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

> On Wed, Apr 08, 2026 at 04:40:03PM -0400, Andres Freund wrote:
> > Note that the whole cached state does automatically get reset at the end of
> > the transaction (AtEOXact_PgStat()->pgstat_clear_snapshot()), just like it did
> > before the shmem stats stuff.
>
> I see a lot of memory used for the pgStatEntryRefHash table, too (e.g., ~16
> MB for 100K tables).  What's interesting is that I cannot reproduce similar
> usage with views like pg_stat_all_tables.  If memory was not a concern, I
> think the "bool *may_free" idea would be fine.

Instead of may_free, which is invasive, what about pgstat_fetch_entry_nocache
which can be called by 2 new APIs pgstat_fetch_stat_tabentry_nocache() and
pgstat_fetch_stat_tabentry_nocache_ext(). This way a caller that uses
these will be required to pfree?

This will allow us to also avoid the GUC override as well in autovacuum.c.

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-08 21:48                                                                       ` Nathan Bossart <[email protected]>
  2026-04-08 23:51                                                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 21:48 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 04:33:00PM -0500, Sami Imseih wrote:
>> On Wed, Apr 08, 2026 at 04:40:03PM -0400, Andres Freund wrote:
>> > Note that the whole cached state does automatically get reset at the end of
>> > the transaction (AtEOXact_PgStat()->pgstat_clear_snapshot()), just like it did
>> > before the shmem stats stuff.
>>
>> I see a lot of memory used for the pgStatEntryRefHash table, too (e.g., ~16
>> MB for 100K tables).  What's interesting is that I cannot reproduce similar
>> usage with views like pg_stat_all_tables.  If memory was not a concern, I
>> think the "bool *may_free" idea would be fine.
> 
> Instead of may_free, which is invasive, what about pgstat_fetch_entry_nocache
> which can be called by 2 new APIs pgstat_fetch_stat_tabentry_nocache() and
> pgstat_fetch_stat_tabentry_nocache_ext(). This way a caller that uses
> these will be required to pfree?

This might help avoid memory usage within a snapshot, but as Andres notes,
this gets released automatically at the end of the transaction.

For a database with 100K tables, here's what I see before calling the view:

          name          | used_bytes
------------------------+------------
 PgStat Shared Ref Hash |       8576
 PgStat Shared Ref      |       2960
 PgStat Pending         |       4712
(3 rows)

After calling the view but before committing the transaction, I see this:

          name          | used_bytes
------------------------+------------
 PgStat Snapshot        |    4194688
 PgStat Shared Ref Hash |    4194688
 PgStat Shared Ref      |    8048240
 PgStat Pending         |       3064
(4 rows)

And after committing, I see:

          name          | used_bytes
------------------------+------------
 PgStat Shared Ref Hash |    4194688
 PgStat Shared Ref      |    8048240
 PgStat Pending         |        360
(3 rows)

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 21:48                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 23:51                                                                         ` Sami Imseih <[email protected]>
  2026-04-09 01:02                                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-09 16:58                                                                           ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  0 siblings, 2 replies; 60+ messages in thread

From: Sami Imseih @ 2026-04-08 23:51 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

> On Wed, Apr 08, 2026 at 04:33:00PM -0500, Sami Imseih wrote:
> >> On Wed, Apr 08, 2026 at 04:40:03PM -0400, Andres Freund wrote:
> >> > Note that the whole cached state does automatically get reset at the end of
> >> > the transaction (AtEOXact_PgStat()->pgstat_clear_snapshot()), just like it did
> >> > before the shmem stats stuff.
> >>
> >> I see a lot of memory used for the pgStatEntryRefHash table, too (e.g., ~16
> >> MB for 100K tables).  What's interesting is that I cannot reproduce similar
> >> usage with views like pg_stat_all_tables.  If memory was not a concern, I
> >> think the "bool *may_free" idea would be fine.
> >
> > Instead of may_free, which is invasive, what about pgstat_fetch_entry_nocache
> > which can be called by 2 new APIs pgstat_fetch_stat_tabentry_nocache() and
> > pgstat_fetch_stat_tabentry_nocache_ext(). This way a caller that uses
> > these will be required to pfree?
>
> This might help avoid memory usage within a snapshot,

Yes, this is exactly why it would be useful for autovacuum workers and
the scores view. relation_needs_vacanalyze() can use the nocache variant,
and we don't need to have a conditional pfree. What I did not like about this
idea after thinking about it more is the performance overhead potentially since
every call to the view will take a shared lock on the entries, and the stats
will not be consistent when the view is called multiple times in a transaction
even when stats_fetch_consistency is NONE. The latter could be a desired
feature, but it goes against the users intentions and could be confusing.

I went ahead and implemented Andres's idea of will_free. Callers of
pgstat_fetch_entry can either pass a NULL to a will_free parameter,
or a bool. Callers that pass the bool can check if will_free is true and
can choose to free the entry.

For now, to keep the changes minimal, I only pgstat_fetch_stat_tabentry_ext()
will call pgstat_fetch_entry() with the bool and relation_needs_vacanalyze()
will be the only call site that checks this to pfree the entry.

There may be some opportunities to improve other call sites if they
are indeed leaking.
For example,  pgstat_copy_relation_stats() could leak with
fetch_consistency = NONE. I kept
that out for now, but we should probably close that gap in another patch.
Also, pgstat_fetch_stat_dbentry() in autovacuum.c could potentially
use this, but I did
not look into detail.

What do you think?

--
Sami


Attachments:

  [application/octet-stream] v2-0001-Fix-double-free-on-relation_needs_vacanalyze.patch (9.2K, 2-v2-0001-Fix-double-free-on-relation_needs_vacanalyze.patch)
  download | inline diff:
From d9dbf585e51274da0a71e0d06460e2330822bd86 Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Wed, 8 Apr 2026 23:36:07 +0000
Subject: [PATCH v2 1/1] Fix double-free on relation_needs_vacanalyze

relation_needs_vacanalyze() unconditionally calls pfree() on the
tabentry returned by pgstat_fetch_stat_tabentry_ext(). This is correct
for autovacuum, which uses PGSTAT_FETCH_CONSISTENCY_NONE where the
caller owns the allocation, but causes a double-free for callers using
PGSTAT_FETCH_CONSISTENCY_CACHE where the entry is managed by the
snapshot cache.

Fix this by adding a bool *will_free output parameter to
pgstat_fetch_entry() and propagating it through
pgstat_fetch_stat_tabentry_ext(). This lets callers determine whether
they are responsible for freeing the returned entry. Callers that
don't need this information pass NULL, keeping existing call sites
minimal while allowing future callers to make use of it as needed.
---
 src/backend/postmaster/autovacuum.c                      | 6 ++++--
 src/backend/utils/activity/pgstat.c                      | 9 ++++++++-
 src/backend/utils/activity/pgstat_backend.c              | 2 +-
 src/backend/utils/activity/pgstat_database.c             | 2 +-
 src/backend/utils/activity/pgstat_function.c             | 2 +-
 src/backend/utils/activity/pgstat_relation.c             | 8 ++++----
 src/backend/utils/activity/pgstat_replslot.c             | 2 +-
 src/backend/utils/activity/pgstat_subscription.c         | 2 +-
 src/include/pgstat.h                                     | 3 ++-
 src/include/utils/pgstat_internal.h                      | 3 ++-
 .../modules/test_custom_stats/test_custom_var_stats.c    | 2 +-
 11 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index bd626a16363..3c1fed6264d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -3080,6 +3080,7 @@ relation_needs_vacanalyze(Oid relid,
 	PgStat_StatTabEntry *tabentry;
 	bool		force_vacuum;
 	bool		av_enabled;
+	bool		will_free;
 
 	/* constants from reloptions or GUC variables */
 	int			vac_base_thresh,
@@ -3246,7 +3247,7 @@ relation_needs_vacanalyze(Oid relid,
 	 * forced.
 	 */
 	tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared,
-											  relid);
+											  relid, &will_free);
 	if (!tabentry)
 		return;
 
@@ -3327,7 +3328,8 @@ relation_needs_vacanalyze(Oid relid,
 			 anltuples, anlthresh, scores->anl,
 			 scores->xid, scores->mxid);
 
-	pfree(tabentry);
+	if (will_free)
+		pfree(tabentry);
 }
 
 /*
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index eb8ccbaa628..a0561250710 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -960,7 +960,7 @@ pgstat_clear_snapshot(void)
 }
 
 void *
-pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
+pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *will_free)
 {
 	PgStat_HashKey key = {0};
 	PgStat_EntryRef *entry_ref;
@@ -971,6 +971,13 @@ pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
 	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
 	Assert(!kind_info->fixed_amount);
 
+	/*
+	 * When pgstat_fetch_consistency is PGSTAT_FETCH_CONSISTENCY_NONE, callers
+	 * will be responsible for freeing the entry.
+	 */
+	if (will_free)
+		*will_free = (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE);
+
 	pgstat_prep_snapshot();
 
 	key.kind = kind;
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 04fe13e64c6..6ffef1040a2 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -95,7 +95,7 @@ pgstat_fetch_stat_backend(ProcNumber procNumber)
 	PgStat_Backend *backend_entry;
 
 	backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
-														  InvalidOid, procNumber);
+														  InvalidOid, procNumber, NULL);
 
 	return backend_entry;
 }
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index 933dcb5cae5..f1846d3236c 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -288,7 +288,7 @@ PgStat_StatDBEntry *
 pgstat_fetch_stat_dbentry(Oid dboid)
 {
 	return (PgStat_StatDBEntry *)
-		pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid);
+		pgstat_fetch_entry(PGSTAT_KIND_DATABASE, dboid, InvalidOid, NULL);
 }
 
 void
diff --git a/src/backend/utils/activity/pgstat_function.c b/src/backend/utils/activity/pgstat_function.c
index e6b84283c6c..d47d05e3d92 100644
--- a/src/backend/utils/activity/pgstat_function.c
+++ b/src/backend/utils/activity/pgstat_function.c
@@ -245,5 +245,5 @@ PgStat_StatFuncEntry *
 pgstat_fetch_stat_funcentry(Oid func_id)
 {
 	return (PgStat_StatFuncEntry *)
-		pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
+		pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id, NULL);
 }
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..01239472f6b 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -61,7 +61,7 @@ pgstat_copy_relation_stats(Relation dst, Relation src)
 	PgStat_EntryRef *dst_ref;
 
 	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
+											  RelationGetRelid(src), NULL);
 	if (!srcstats)
 		return;
 
@@ -468,7 +468,7 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta)
 PgStat_StatTabEntry *
 pgstat_fetch_stat_tabentry(Oid relid)
 {
-	return pgstat_fetch_stat_tabentry_ext(IsSharedRelation(relid), relid);
+	return pgstat_fetch_stat_tabentry_ext(IsSharedRelation(relid), relid, NULL);
 }
 
 /*
@@ -476,12 +476,12 @@ pgstat_fetch_stat_tabentry(Oid relid)
  * whether the to-be-accessed table is a shared relation or not.
  */
 PgStat_StatTabEntry *
-pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid)
+pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, bool *will_free)
 {
 	Oid			dboid = (shared ? InvalidOid : MyDatabaseId);
 
 	return (PgStat_StatTabEntry *)
-		pgstat_fetch_entry(PGSTAT_KIND_RELATION, dboid, reloid);
+		pgstat_fetch_entry(PGSTAT_KIND_RELATION, dboid, reloid, will_free);
 }
 
 /*
diff --git a/src/backend/utils/activity/pgstat_replslot.c b/src/backend/utils/activity/pgstat_replslot.c
index 168ef8f8f45..e09bb6b4355 100644
--- a/src/backend/utils/activity/pgstat_replslot.c
+++ b/src/backend/utils/activity/pgstat_replslot.c
@@ -208,7 +208,7 @@ pgstat_fetch_replslot(NameData slotname)
 
 	if (idx != -1)
 		slotentry = (PgStat_StatReplSlotEntry *) pgstat_fetch_entry(PGSTAT_KIND_REPLSLOT,
-																	InvalidOid, idx);
+																	InvalidOid, idx, NULL);
 
 	LWLockRelease(ReplicationSlotControlLock);
 
diff --git a/src/backend/utils/activity/pgstat_subscription.c b/src/backend/utils/activity/pgstat_subscription.c
index 3277cf88a4e..3eaf3e0390f 100644
--- a/src/backend/utils/activity/pgstat_subscription.c
+++ b/src/backend/utils/activity/pgstat_subscription.c
@@ -107,7 +107,7 @@ PgStat_StatSubEntry *
 pgstat_fetch_stat_subscription(Oid subid)
 {
 	return (PgStat_StatSubEntry *)
-		pgstat_fetch_entry(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid);
+		pgstat_fetch_entry(PGSTAT_KIND_SUBSCRIPTION, InvalidOid, subid, NULL);
 }
 
 /*
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2786a7c5ffb..a42d2124080 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -763,7 +763,8 @@ extern void pgstat_twophase_postabort(FullTransactionId fxid, uint16 info,
 
 extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid);
 extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry_ext(bool shared,
-														   Oid reloid);
+														   Oid reloid,
+														   bool *will_free);
 extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
 
 
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index c0496f2c69c..1669644fe8a 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -685,7 +685,8 @@ extern PgStat_EntryRef *pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid,
 extern PgStat_EntryRef *pgstat_fetch_pending_entry(PgStat_Kind kind,
 												   Oid dboid, uint64 objid);
 
-extern void *pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid);
+extern void *pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid,
+								bool *will_free);
 extern void pgstat_snapshot_fixed(PgStat_Kind kind);
 
 
diff --git a/src/test/modules/test_custom_stats/test_custom_var_stats.c b/src/test/modules/test_custom_stats/test_custom_var_stats.c
index 2ef0e903745..95ec7687c6c 100644
--- a/src/test/modules/test_custom_stats/test_custom_var_stats.c
+++ b/src/test/modules/test_custom_stats/test_custom_var_stats.c
@@ -494,7 +494,7 @@ test_custom_stats_var_fetch_entry(const char *stat_name)
 	return (PgStat_StatCustomVarEntry *)
 		pgstat_fetch_entry(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS,
 						   InvalidOid,
-						   PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name));
+						   PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), NULL);
 }
 
 /*--------------------------------------------------------------------------
-- 
2.50.1



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 21:48                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 23:51                                                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-09 01:02                                                                           ` Nathan Bossart <[email protected]>
  2026-04-09 16:34                                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  1 sibling, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-09 01:02 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 06:51:59PM -0500, Sami Imseih wrote:
> I went ahead and implemented Andres's idea of will_free. Callers of
> pgstat_fetch_entry can either pass a NULL to a will_free parameter,
> or a bool. Callers that pass the bool can check if will_free is true and
> can choose to free the entry.

Yeah, I think this is the right thing to do.  IMHO "may_free" is slightly
more accurate here, since we're telling the caller that they can explicitly
pfree() the result instead of letting the pgstat machinery or their own
memory context take care of it.

+	/*
+	 * When pgstat_fetch_consistency is PGSTAT_FETCH_CONSISTENCY_NONE, callers
+	 * will be responsible for freeing the entry.
+	 */
+	if (will_free)
+		*will_free = (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE);

I don't know if this is a strict project guideline, but when I add these
sorts of function parameters I usually just require the caller to provide a
non-NULL pointer.  But... what you have here seems fine, too.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 21:48                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 23:51                                                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-09 01:02                                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-09 16:34                                                                             ` Nathan Bossart <[email protected]>
  2026-04-09 18:10                                                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  0 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-09 16:34 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Here's what I have staged for commit.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 21:48                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 23:51                                                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-09 01:02                                                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-09 16:34                                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-09 18:10                                                                               ` Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 60+ messages in thread

From: Nathan Bossart @ 2026-04-09 18:10 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Committed.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:33                                                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-08 21:48                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 23:51                                                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-04-09 16:58                                                                           ` Andres Freund <[email protected]>
  1 sibling, 0 replies; 60+ messages in thread

From: Andres Freund @ 2026-04-09 16:58 UTC (permalink / raw)
  To: Sami Imseih <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Hi,

On 2026-04-08 18:51:59 -0500, Sami Imseih wrote:
> There may be some opportunities to improve other call sites if they
> are indeed leaking.
> For example,  pgstat_copy_relation_stats() could leak with
> fetch_consistency = NONE.

Hard to believe that could be relevant, given it's used when reindexing
concurrently.  Compared to the amount of memory that will use, leaving a small
bit of memory around until the end of the statement is likely hard to even
measure.


> I kept that out for now, but we should probably close that gap in another
> patch.  Also, pgstat_fetch_stat_dbentry() in autovacuum.c could potentially
> use this, but I did not look into detail.

Probably fine, it's a temporary context that's just used for building the
list. You'd have to have a lot of databases for this to be a relevant
factor. The whole function fundamentally uses O(databases) memory, making the
constant factor a bit bigger doesn't seem reelvant.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-08 21:59                                                                     ` Andres Freund <[email protected]>
  2026-04-08 22:23                                                                       ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-09 00:57                                                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  1 sibling, 2 replies; 60+ messages in thread

From: Andres Freund @ 2026-04-08 21:59 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Hi,

On 2026-04-08 16:23:45 -0500, Nathan Bossart wrote:
> On Wed, Apr 08, 2026 at 04:40:03PM -0400, Andres Freund wrote:
> > Note that the whole cached state does automatically get reset at the end of
> > the transaction (AtEOXact_PgStat()->pgstat_clear_snapshot()), just like it did
> > before the shmem stats stuff.
>
> I see a lot of memory used for the pgStatEntryRefHash table, too (e.g., ~16
> MB for 100K tables).

That's not the stats snapshot data. That's basically a semi-permanent pin on
the shared stats entry so that we don't continually need to do lookups in the
shared stats hash table. Without that you'd have a *lot* of contention on the
shared hash table and the DSA for the stats entries themselves.

That's right now just the price you pay for accessing and generating stats. It
was way more before shared memory stats (as in a factor of 10 or so, and it
was happening way more often and it caused the full database's stats worth of
memory usage even if you just accessed a portion of the stats).


It's possible that we could more frequently clean out references, but when to
precisely do that and to how much is not a trivial problem - we haven't
tackled it for our catcache/relcaches either.


I don't think it should be quite 16MB for 100k tables though? I see

┌────────────────────────┬────────────────┐
│          name          │ pg_size_pretty │
├────────────────────────┼────────────────┤
│ PgStat Shared Ref      │ 8104 kB        │
│ PgStat Shared Ref Hash │ 4097 kB        │
│ CacheMemoryContext     │ 1024 kB        │
└────────────────────────┴────────────────┘

after doing

SELECT sum(score) FROM pg_stat_autovacuum_scores;

in this database:

SELECT relkind, count(*) FROM pg_class GROUP BY relkind;
┌─────────┬────────┐
│ relkind │ count  │
├─────────┼────────┤
│ S       │      1 │
│ i       │    182 │
│ r       │ 102292 │
│ t       │     43 │
│ v       │    167 │
└─────────┴────────┘
(5 rows)


> What's interesting is that I cannot reproduce similar usage with views like
> pg_stat_all_tables.

Hm? That would be very surprising.  Is it possible you used LIMIT 1 or such?
The way the pg_stat_all_tables view works it only accesses stats data for
returned rows (because it does all the stats lookups with individiual columns,
which also makes it really slow, but avoids having to form datums for not
returned columns).



in a new session:

postgres[3109075][1]=# SELECT sum(seq_tup_read) FROM pg_stat_all_tables LIMIT 10;
┌────────────┐
│    sum     │
├────────────┤
│ 3223043706 │
└────────────┘
(1 row)

Time: 723.223 ms
postgres[3109075][1]=# SELECT name, pg_size_pretty(total_bytes) FROM pg_backend_memory_contexts ORDER BY total_bytes DESC LIMIT 3;
┌────────────────────────┬────────────────┐
│          name          │ pg_size_pretty │
├────────────────────────┼────────────────┤
│ PgStat Shared Ref      │ 8104 kB        │
│ PgStat Shared Ref Hash │ 4097 kB        │
│ CacheMemoryContext     │ 1024 kB        │
└────────────────────────┴────────────────┘

I.e. byte for byte identical to the av case.



> If memory was not a concern, I think the "bool *may_free" idea would be
> fine.  But assuming it is, we will probably need to do something more
> creative.

I don't think you're going to be able to quickly do anything about this.  I
think this is a completely independent issue of the *may_free thing.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:59                                                                     ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
@ 2026-04-08 22:23                                                                       ` Andres Freund <[email protected]>
  1 sibling, 0 replies; 60+ messages in thread

From: Andres Freund @ 2026-04-08 22:23 UTC (permalink / raw)
  To: Nathan Bossart <[email protected]>; +Cc: Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

Hi,

On 2026-04-08 17:59:36 -0400, Andres Freund wrote:
> I don't think it should be quite 16MB for 100k tables though? I see
> 
> ┌────────────────────────┬────────────────┐
> │          name          │ pg_size_pretty │
> ├────────────────────────┼────────────────┤
> │ PgStat Shared Ref      │ 8104 kB        │
> │ PgStat Shared Ref Hash │ 4097 kB        │
> │ CacheMemoryContext     │ 1024 kB        │
> └────────────────────────┴────────────────┘

> after doing
> 
> SELECT sum(score) FROM pg_stat_autovacuum_scores;
> 
> in this database:
> 
> SELECT relkind, count(*) FROM pg_class GROUP BY relkind;
> ┌─────────┬────────┐
> │ relkind │ count  │
> ├─────────┼────────┤
> │ S       │      1 │
> │ i       │    182 │
> │ r       │ 102292 │
> │ t       │     43 │
> │ v       │    167 │
> └─────────┴────────┘
> (5 rows)

(8104 * 1024) / 102292 = 81.13

81 bytes eemed a bit high, given the struct is 48 bytes.

My first thought is that this was from a debug build, where allocations have
more overhead.  And indeed, in an optimized build it's "just" 7248kB, a
per-entry size of 78.78.

A lot of that is probably due to rounding up in aset.c (and perhaps a bit due
to ALLOCSET_SMALL_SIZES).

Since "PgStat Shared Ref" only ever does one type of allocation, it actually
is a good candidate for slab.  In debug that's 7248kB and optimized it's
5632kB, when using a 16kB block size.  The latter is 56 bytes per entry, where
sizeof(PgStat_EntryRef) is 48 bytes. Which is a pretty reasonable allocator
overhead and 16kB seems not too crazy an allocator block size for this?


Note that even if you just \dt in that database, you have a CacheMemoryContext
of 41MB.  If you VACUUM the caches are 121MB.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 20:40                                                                 ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
  2026-04-08 21:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 21:59                                                                     ` Re: Add pg_stat_autovacuum_priority Andres Freund <[email protected]>
@ 2026-04-09 00:57                                                                       ` Nathan Bossart <[email protected]>
  1 sibling, 0 replies; 60+ messages in thread

From: Nathan Bossart @ 2026-04-09 00:57 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Tom Lane <[email protected]>; Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 05:59:36PM -0400, Andres Freund wrote:
> On 2026-04-08 16:23:45 -0500, Nathan Bossart wrote:
>> What's interesting is that I cannot reproduce similar usage with views like
>> pg_stat_all_tables.
> 
> Hm? That would be very surprising.  Is it possible you used LIMIT 1 or such?
> The way the pg_stat_all_tables view works it only accesses stats data for
> returned rows (because it does all the stats lookups with individiual columns,
> which also makes it really slow, but avoids having to form datums for not
> returned columns).

*facepalm*

I was using count(*).  If I do something like sum(n_dead_tup), I see much
more memory used by pg_stat_all_tables.  Sorry for the noise.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
@ 2026-04-08 21:19                                                                 ` Nathan Bossart <[email protected]>
  2026-04-09 15:23                                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2 siblings, 1 reply; 60+ messages in thread

From: Nathan Bossart @ 2026-04-08 21:19 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 03:21:38PM -0400, Tom Lane wrote:
> Nathan Bossart <[email protected]> writes:
>> FWIW that elog() shouldn't be emitting anything from the view, unless
>> something is broken.
> 
> [ looks again... ]  Oh, you mean because pg_stat_get_autovacuum_scores
> passes 0 for elevel instead of any of the valid elevel constants.
> Can you say "undocumented cowboy hack"?  I'd be happier if we added
> something like "#define LOG_NEVER 0  /* Never emit this message */"
> to elog.h and used that.

Yeah... I should've done that originally.  Here's a patch that I'll apply
in the next day or so, barring objections.

-- 
nathan


^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
  2026-04-08 17:17                                                       ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 17:56                                                         ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 18:53                                                           ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 19:09                                                             ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 19:21                                                               ` Re: Add pg_stat_autovacuum_priority Tom Lane <[email protected]>
  2026-04-08 21:19                                                                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
@ 2026-04-09 15:23                                                                   ` Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 60+ messages in thread

From: Nathan Bossart @ 2026-04-09 15:23 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Sami Imseih <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers; [email protected]

On Wed, Apr 08, 2026 at 04:19:57PM -0500, Nathan Bossart wrote:
> On Wed, Apr 08, 2026 at 03:21:38PM -0400, Tom Lane wrote:
>> Nathan Bossart <[email protected]> writes:
>>> FWIW that elog() shouldn't be emitting anything from the view, unless
>>> something is broken.
>> 
>> [ looks again... ]  Oh, you mean because pg_stat_get_autovacuum_scores
>> passes 0 for elevel instead of any of the valid elevel constants.
>> Can you say "undocumented cowboy hack"?  I'd be happier if we added
>> something like "#define LOG_NEVER 0  /* Never emit this message */"
>> to elog.h and used that.
> 
> Yeah... I should've done that originally.  Here's a patch that I'll apply
> in the next day or so, barring objections.

Committed.  Working on Sami's patch next.

-- 
nathan





^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-28 09:18 ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-28 17:53   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 02:09     ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-30 12:56       ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 15:16         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-30 17:13           ` Re: Add pg_stat_autovacuum_priority Robert Treat <[email protected]>
  2026-03-30 18:16             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-03-31 18:09               ` Re: Add pg_stat_autovacuum_priority Bharath Rupireddy <[email protected]>
  2026-03-31 18:34                 ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 02:46                   ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 15:07                     ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 16:58                       ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 18:45                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-01 21:28                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-01 23:04                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-03 16:47                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-03 19:13                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 02:40                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 13:25                                     ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 14:33                                       ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 15:32                                         ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 16:33                                           ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 17:48                                             ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-04 19:10                                               ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-04 22:35                                                 ` Re: Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
  2026-04-06 21:58                                                   ` Re: Add pg_stat_autovacuum_priority Nathan Bossart <[email protected]>
  2026-04-08 17:00                                                     ` Re: Add pg_stat_autovacuum_priority Alexander Lakhin <[email protected]>
@ 2026-04-08 18:14                                                       ` Sami Imseih <[email protected]>
  1 sibling, 0 replies; 60+ messages in thread

From: Sami Imseih @ 2026-04-08 18:14 UTC (permalink / raw)
  To: Alexander Lakhin <[email protected]>; +Cc: Nathan Bossart <[email protected]>; Bharath Rupireddy <[email protected]>; Robert Treat <[email protected]>; [email protected]; pgsql-hackers

> 07.04.2026 00:58, Nathan Bossart wrote:
> > Committed after some more editorialization.
>
> Please look at a new anomaly, I and SQLsmith have discovered:
> SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
>      (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
> ERROR:  detected double pfree in PgStat Snapshot 0x5f6fa4d95d50

oops, nice catch!

With the default stats_fetch_consistency setting of
PGSTAT_FETCH_CONSISTENCY_CACHE,
the stats returned by pgstat_fetch_entry() are allocated in the
snapshot's memory context,
so they get free'd when the snapshot is cleared by pgstat_clear_snapshot().
That means inside relation_needs_vacanalyze() we should only
pfree(tabentry) when
pgstat_fetch_consistency is PGSTAT_FETCH_CONSISTENCY_NONE,
as in that mode the stats are palloc'd in the caller's memory context
and must be freed explicitly.

autovacuum.c forces pgstat_fetch_consistency to PGSTAT_FETCH_CONSISTENCY_NONE
for the autovacuum launcher, so the pfree() was never an issue there.
I don't think we should do the same for pg_stat_get_autovacuum_scores
, as we should
not override the users intentions for fetch consistency, and also it
would complicate the
view greatly as we must force a _NONE consistency and a PG_TRY/PG_CATCH
to restore the original mode.

Attached is the fix:

postgres=# set stats_fetch_consistency = NONE;
SET
postgres=# SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
     (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
  score  |  score
---------+---------
 8.5e-08 | 8.5e-08
(1 row)

postgres=# set stats_fetch_consistency = snapshot;
SET
postgres=# SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
     (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
  score  |  score
---------+---------
 8.5e-08 | 8.5e-08
(1 row)

postgres=# set stats_fetch_consistency = cache;
SET
postgres=# SELECT (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1),
     (SELECT score FROM pg_stat_get_autovacuum_scores() LIMIT 1);
  score  |  score
---------+---------
 8.5e-08 | 8.5e-08
(1 row)


Attachments:

  [application/octet-stream] v1-0001-Fix-double-free-in-relation_needs_vacanalyze.patch (1.4K, 2-v1-0001-Fix-double-free-in-relation_needs_vacanalyze.patch)
  download | inline diff:
From abc19158da02689c3c12082519bf85f11ab96feb Mon Sep 17 00:00:00 2001
From: Sami Imseih <[email protected]>
Date: Wed, 8 Apr 2026 18:06:46 +0000
Subject: [PATCH v1 1/1] Fix double free in relation_needs_vacanalyze

When relation_needs_vacanalyze is executed multiple times with
stat_fetch_consistency other than NONE, a double free occurs
for the table stats. First in relation_needs_vacanalyze, then
when the snapshot's memory context is freed. In the NONE case,
the snapshot is stored in the caller's memory context, so it's
the caller's responsibility to free the memory.

Fix this by only calling pfree inside relation_needs_vacanalyze
when stat_fetch_consistency is NONE.
---
 src/backend/postmaster/autovacuum.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index bd626a16363..b1d12e3a61c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -3327,7 +3327,13 @@ relation_needs_vacanalyze(Oid relid,
 			 anltuples, anlthresh, scores->anl,
 			 scores->xid, scores->mxid);
 
-	pfree(tabentry);
+	/*
+	 * In PGSTAT_FETCH_CONSISTENCY_NONE mode, stats are palloc'd in the
+	 * caller's memory context and must be freed explicitly.  In other modes,
+	 * they are managed by the snapshot's memory context.
+	 */
+	if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
+		pfree(tabentry);
 }
 
 /*
-- 
2.50.1



^ permalink  raw  reply  [nested|flat] 60+ messages in thread

* Re: Add pg_stat_autovacuum_priority
  2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
@ 2026-03-30 15:32 ` Sami Imseih <[email protected]>
  2 siblings, 0 replies; 60+ messages in thread

From: Sami Imseih @ 2026-03-30 15:32 UTC (permalink / raw)
  To: Álvaro Herrera <[email protected]>; +Cc: pgsql-hackers

> > 0002 introduces the view with documentation and testing in
> > vacuum.c (xid age and mxid age scores are not tested as
> > they require xid_wraparound to consume enough XIDs to
> > trigger a score, which will cost too much time for a
> > regression test).
>
> After this patch, there are three copies of the combined call to
> pgstat_fetch_stat_tabentry_ext() and relation_needs_vacanalyze().  Can
> we avoid that, ideally reducing to only one such?  Or, at least, it
> looks like recheck_relation_needs_vacanalyze() may no longer need to
> exist at all.

recheck_relation_needs_vacanalyze() can be removed and replace
with compute_autovac_score(). I was trying to minimize refactoring,
but this seems worthwhile to do.

--
Sami





^ permalink  raw  reply  [nested|flat] 60+ messages in thread


end of thread, other threads:[~2026-04-09 18:10 UTC | newest]

Thread overview: 60+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-03-27 23:14 Add pg_stat_autovacuum_priority Sami Imseih <[email protected]>
2026-03-28 04:14 ` SATYANARAYANA NARLAPURAM <[email protected]>
2026-03-28 04:19   ` SATYANARAYANA NARLAPURAM <[email protected]>
2026-03-28 09:18 ` Bharath Rupireddy <[email protected]>
2026-03-28 17:53   ` Sami Imseih <[email protected]>
2026-03-30 02:09     ` Bharath Rupireddy <[email protected]>
2026-03-30 12:56       ` Robert Treat <[email protected]>
2026-03-30 15:16         ` Sami Imseih <[email protected]>
2026-03-30 17:13           ` Robert Treat <[email protected]>
2026-03-30 18:16             ` Sami Imseih <[email protected]>
2026-03-31 15:38               ` Nathan Bossart <[email protected]>
2026-03-31 16:15                 ` Sami Imseih <[email protected]>
2026-03-31 16:28                   ` Nathan Bossart <[email protected]>
2026-03-31 16:41                     ` Sami Imseih <[email protected]>
2026-03-31 18:09               ` Bharath Rupireddy <[email protected]>
2026-03-31 18:34                 ` Nathan Bossart <[email protected]>
2026-04-01 02:46                   ` Sami Imseih <[email protected]>
2026-04-01 15:07                     ` Nathan Bossart <[email protected]>
2026-04-01 16:58                       ` Sami Imseih <[email protected]>
2026-04-01 18:45                         ` Sami Imseih <[email protected]>
2026-04-01 21:28                           ` Nathan Bossart <[email protected]>
2026-04-01 23:04                             ` Sami Imseih <[email protected]>
2026-04-03 16:47                               ` Nathan Bossart <[email protected]>
2026-04-03 19:13                                 ` Sami Imseih <[email protected]>
2026-04-04 02:40                                   ` Nathan Bossart <[email protected]>
2026-04-04 13:25                                     ` Sami Imseih <[email protected]>
2026-04-04 14:33                                       ` Nathan Bossart <[email protected]>
2026-04-04 15:32                                         ` Sami Imseih <[email protected]>
2026-04-04 16:33                                           ` Nathan Bossart <[email protected]>
2026-04-04 17:48                                             ` Sami Imseih <[email protected]>
2026-04-04 19:10                                               ` Nathan Bossart <[email protected]>
2026-04-04 22:35                                                 ` Sami Imseih <[email protected]>
2026-04-06 21:58                                                   ` Nathan Bossart <[email protected]>
2026-04-08 17:00                                                     ` Alexander Lakhin <[email protected]>
2026-04-08 17:17                                                       ` Tom Lane <[email protected]>
2026-04-08 17:56                                                         ` Nathan Bossart <[email protected]>
2026-04-08 18:18                                                           ` Nathan Bossart <[email protected]>
2026-04-08 18:28                                                             ` Sami Imseih <[email protected]>
2026-04-08 18:37                                                               ` Nathan Bossart <[email protected]>
2026-04-08 18:44                                                                 ` Andres Freund <[email protected]>
2026-04-08 18:53                                                           ` Tom Lane <[email protected]>
2026-04-08 19:09                                                             ` Nathan Bossart <[email protected]>
2026-04-08 19:21                                                               ` Tom Lane <[email protected]>
2026-04-08 19:35                                                                 ` Sami Imseih <[email protected]>
2026-04-08 20:40                                                                 ` Andres Freund <[email protected]>
2026-04-08 21:23                                                                   ` Nathan Bossart <[email protected]>
2026-04-08 21:33                                                                     ` Sami Imseih <[email protected]>
2026-04-08 21:48                                                                       ` Nathan Bossart <[email protected]>
2026-04-08 23:51                                                                         ` Sami Imseih <[email protected]>
2026-04-09 01:02                                                                           ` Nathan Bossart <[email protected]>
2026-04-09 16:34                                                                             ` Nathan Bossart <[email protected]>
2026-04-09 18:10                                                                               ` Nathan Bossart <[email protected]>
2026-04-09 16:58                                                                           ` Andres Freund <[email protected]>
2026-04-08 21:59                                                                     ` Andres Freund <[email protected]>
2026-04-08 22:23                                                                       ` Andres Freund <[email protected]>
2026-04-09 00:57                                                                       ` Nathan Bossart <[email protected]>
2026-04-08 21:19                                                                 ` Nathan Bossart <[email protected]>
2026-04-09 15:23                                                                   ` Nathan Bossart <[email protected]>
2026-04-08 18:14                                                       ` Sami Imseih <[email protected]>
2026-03-30 15:32 ` Sami Imseih <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox