agora inbox for [email protected]
help / color / mirror / Atom feed[PATCH] Track skipped tables during autovacuum and autoanalyze
275+ messages / 2 participants
[nested] [flat]
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH] Track skipped tables during autovacuum and autoanalyze
@ 2026-03-24 04:09 Yugo Nagata <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Yugo Nagata @ 2026-03-24 04:09 UTC (permalink / raw)
---
src/backend/catalog/system_views.sql | 4 ++
src/backend/commands/vacuum.c | 10 +++
src/backend/utils/activity/pgstat_relation.c | 66 ++++++++++++++++++++
src/backend/utils/adt/pgstatfuncs.c | 12 ++++
src/include/catalog/pg_proc.dat | 16 +++++
src/include/pgstat.h | 8 +++
src/test/regress/expected/rules.out | 12 ++++
7 files changed, 128 insertions(+)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index f1ed7b58f13..914ba762b68 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -737,12 +737,16 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(C.oid) as last_skipped_autovacuum,
pg_stat_get_last_analyze_time(C.oid) as last_analyze,
pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(C.oid) as last_skipped_autoanalyze,
pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(C.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(C.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(C.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(C.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(C.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(C.oid) AS total_analyze_time,
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index bce3a2daa24..5a0b5db39ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -829,10 +829,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_VACUUM) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_vacuum(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
@@ -850,10 +855,15 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options,
if ((options & VACOPT_ANALYZE) != 0)
{
if (!rel_lock)
+ {
ereport(elevel,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
relation->relname)));
+
+ if (AmAutoVacuumWorkerProcess())
+ pgstat_report_skipped_analyze(relid);
+ }
else
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_TABLE),
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index bc8c43b96aa..1329f7d9bf1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -271,6 +271,72 @@ pgstat_report_vacuum(Relation rel, PgStat_Counter livetuples,
(void) pgstat_flush_backend(false, PGSTAT_BACKEND_FLUSH_IO);
}
+/*
+ * Report that the table was skipped during autovacuum.
+ */
+void
+pgstat_report_skipped_vacuum(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autovacuum_time = ts;
+ tabentry->skipped_autovacuum_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
+/*
+ * Report that the table was skipped during autoanalyze.
+ */
+void
+pgstat_report_skipped_analyze(Oid relid)
+{
+ PgStat_EntryRef *entry_ref;
+ PgStatShared_Relation *shtabentry;
+ PgStat_StatTabEntry *tabentry;
+ TimestampTz ts;
+
+ Assert(AmAutoVacuumWorkerProcess());
+
+ if (!pgstat_track_counts)
+ return;
+
+ /* Store the data in the table's hash table entry. */
+ ts = GetCurrentTimestamp();
+
+ /* block acquiring lock for the same reason as pgstat_report_autovac() */
+ entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, MyDatabaseId,
+ relid, false);
+
+ shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+ tabentry = &shtabentry->stats;
+
+
+ tabentry->last_skipped_autoanalyze_time = ts;
+ tabentry->skipped_autoanalyze_count++;
+
+ pgstat_unlock_entry(entry_ref);
+}
+
/*
* Report that the table was just analyzed and flush IO statistics.
*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5f907335990..ae8e8fd90f6 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -84,6 +84,12 @@ PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
/* pg_stat_get_numscans */
PG_STAT_GET_RELENTRY_INT64(numscans)
+/* pg_stat_get_skipped_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autoanalyze_count)
+
+/* pg_stat_get_skipped_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(skipped_autovacuum_count)
+
/* pg_stat_get_tuples_deleted */
PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
@@ -170,6 +176,12 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_last_skipped_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autoanalyze_time)
+
+/* pg_stat_get_last_skipped_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_skipped_autovacuum_time)
+
/* pg_stat_get_stat_reset_time */
PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat_reset_time)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 84e7adde0e5..3ca1d6762f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5667,6 +5667,22 @@
proname => 'pg_stat_get_total_autoanalyze_time', provolatile => 's',
proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
prosrc => 'pg_stat_get_total_autoanalyze_time' },
+{ oid => '8142', descr => 'statistics: last skipped auto analyze time for a table',
+ proname => 'pg_stat_get_last_skipped_autoanalyze_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autoanalyze_time' },
+{ oid => '8143', descr => 'statistics: last skipped auto vacuum time for a table',
+ proname => 'pg_stat_get_last_skipped_autovacuum_time', provolatile => 's',
+ proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_last_skipped_autovacuum_time' },
+{ oid => '8144', descr => 'statistics: number of skipped auto analyzes for a table',
+ proname => 'pg_stat_get_skipped_autoanalyze_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autoanalyze_count' },
+{ oid => '8145', descr => 'statistics: number of skipped auto vacuum for a table',
+ proname => 'pg_stat_get_skipped_autovacuum_count', provolatile => 's',
+ proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+ prosrc => 'pg_stat_get_skipped_autovacuum_count' },
{ 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/include/pgstat.h b/src/include/pgstat.h
index 216b93492ba..bd6be54dfb3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -460,6 +460,11 @@ typedef struct PgStat_StatTabEntry
TimestampTz last_autoanalyze_time; /* autovacuum initiated */
PgStat_Counter autoanalyze_count;
+ TimestampTz last_skipped_autovacuum_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autovacuum_count;
+ TimestampTz last_skipped_autoanalyze_time; /* autovacuum initiated */
+ PgStat_Counter skipped_autoanalyze_count;
+
PgStat_Counter total_vacuum_time; /* times in milliseconds */
PgStat_Counter total_autovacuum_time;
PgStat_Counter total_analyze_time;
@@ -679,6 +684,9 @@ extern void pgstat_report_analyze(Relation rel,
PgStat_Counter livetuples, PgStat_Counter deadtuples,
bool resetcounter, TimestampTz starttime);
+extern void pgstat_report_skipped_vacuum(Oid relid);
+extern void pgstat_report_skipped_analyze(Oid relid);
+
/*
* If stats are enabled, but pending data hasn't been prepared yet, call
* pgstat_assoc_relation() to do so. See its comment for why this is done
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 32bea58db2c..1ca129ad2a4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1836,12 +1836,16 @@ pg_stat_all_tables| SELECT c.oid AS relid,
pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
+ pg_stat_get_last_skipped_autovacuum_time(c.oid) AS last_skipped_autovacuum,
pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+ pg_stat_get_last_skipped_autoanalyze_time(c.oid) AS last_skipped_autoanalyze,
pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
+ pg_stat_get_skipped_autovacuum_count(c.oid) AS skipped_autovacuum_count,
pg_stat_get_analyze_count(c.oid) AS analyze_count,
pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count,
+ pg_stat_get_skipped_autoanalyze_count(c.oid) AS skipped_autoanalyze_count,
pg_stat_get_total_vacuum_time(c.oid) AS total_vacuum_time,
pg_stat_get_total_autovacuum_time(c.oid) AS total_autovacuum_time,
pg_stat_get_total_analyze_time(c.oid) AS total_analyze_time,
@@ -2288,12 +2292,16 @@ pg_stat_sys_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
@@ -2343,12 +2351,16 @@ pg_stat_user_tables| SELECT relid,
n_ins_since_vacuum,
last_vacuum,
last_autovacuum,
+ last_skipped_autovacuum,
last_analyze,
last_autoanalyze,
+ last_skipped_autoanalyze,
vacuum_count,
autovacuum_count,
+ skipped_autovacuum_count,
analyze_count,
autoanalyze_count,
+ skipped_autoanalyze_count,
total_vacuum_time,
total_autovacuum_time,
total_analyze_time,
--
2.43.0
--Multipart=_Tue__24_Mar_2026_15_11_33_+0900_PyndxOyIILJh_XYK--
^ permalink raw reply [nested|flat] 275+ messages in thread
* [PATCH v50 2/8] Rename cluster.c/h -> repack.c/h
@ 2026-03-31 16:55 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 275+ messages in thread
From: Álvaro Herrera @ 2026-03-31 16:55 UTC (permalink / raw)
---
src/backend/commands/Makefile | 2 +-
src/backend/commands/matview.c | 2 +-
src/backend/commands/meson.build | 2 +-
src/backend/commands/{cluster.c => repack.c} | 6 +++---
src/backend/commands/tablecmds.c | 2 +-
src/backend/commands/vacuum.c | 6 +++---
src/backend/storage/ipc/procsignal.c | 1 +
src/backend/tcop/postgres.c | 1 +
src/backend/tcop/utility.c | 2 +-
src/include/commands/{cluster.h => repack.h} | 12 ++++++------
10 files changed, 19 insertions(+), 17 deletions(-)
rename src/backend/commands/{cluster.c => repack.c} (99%)
rename src/include/commands/{cluster.h => repack.h} (90%)
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index c10fdba2bbb..fe1bba3a9b9 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -18,7 +18,6 @@ OBJS = \
amcmds.o \
analyze.o \
async.o \
- cluster.o \
collationcmds.o \
comment.o \
constraint.o \
@@ -51,6 +50,7 @@ OBJS = \
proclang.o \
propgraphcmds.o \
publicationcmds.o \
+ repack.o \
schemacmds.o \
seclabel.o \
sequence.o \
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index d3be8939011..5db4fe75dce 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -24,8 +24,8 @@
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
-#include "commands/cluster.h"
#include "commands/matview.h"
+#include "commands/repack.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "executor/executor.h"
diff --git a/src/backend/commands/meson.build b/src/backend/commands/meson.build
index 90c7e37a429..f624aae74af 100644
--- a/src/backend/commands/meson.build
+++ b/src/backend/commands/meson.build
@@ -6,7 +6,6 @@ backend_sources += files(
'amcmds.c',
'analyze.c',
'async.c',
- 'cluster.c',
'collationcmds.c',
'comment.c',
'constraint.c',
@@ -39,6 +38,7 @@ backend_sources += files(
'proclang.c',
'propgraphcmds.c',
'publicationcmds.c',
+ 'repack.c',
'schemacmds.c',
'seclabel.c',
'sequence.c',
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/repack.c
similarity index 99%
rename from src/backend/commands/cluster.c
rename to src/backend/commands/repack.c
index f241e18b153..20f0a572236 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/repack.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * cluster.c
+ * repack.c
* REPACK a table; formerly known as CLUSTER. VACUUM FULL also uses
* parts of this code.
*
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * src/backend/commands/cluster.c
+ * src/backend/commands/repack.c
*
*-------------------------------------------------------------------------
*/
@@ -33,9 +33,9 @@
#include "catalog/pg_am.h"
#include "catalog/pg_inherits.h"
#include "catalog/toasting.h"
-#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/progress.h"
+#include "commands/repack.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0ce2e81f9c2..e2882a50b3b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -57,10 +57,10 @@
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
-#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
+#include "commands/repack.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 0ed363d1c85..b179b62b5c8 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -9,7 +9,7 @@
*
* VACUUM for heap AM is implemented in vacuumlazy.c, parallel vacuum in
* vacuumparallel.c, ANALYZE in analyze.c, and VACUUM FULL is a variant of
- * CLUSTER, handled in cluster.c.
+ * REPACK, handled in repack.c.
*
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
@@ -38,9 +38,9 @@
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "commands/async.h"
-#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/progress.h"
+#include "commands/repack.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -2293,7 +2293,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
if ((params.options & VACOPT_VERBOSE) != 0)
cluster_params.options |= CLUOPT_VERBOSE;
- /* VACUUM FULL is a variant of REPACK; see cluster.c */
+ /* VACUUM FULL is a variant of REPACK; see repack.c */
cluster_rel(REPACK_COMMAND_VACUUMFULL, rel, InvalidOid,
&cluster_params);
/* cluster_rel closes the relation, but keeps lock */
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7e017c8d53b..7cef6e43661 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -19,6 +19,7 @@
#include "access/parallel.h"
#include "commands/async.h"
+#include "commands/repack.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "port/pg_bitutils.h"
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 10be60011ad..9fbaa5c00f0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -39,6 +39,7 @@
#include "commands/event_trigger.h"
#include "commands/explain_state.h"
#include "commands/prepare.h"
+#include "commands/repack.h"
#include "common/pg_prng.h"
#include "jit/jit.h"
#include "libpq/libpq.h"
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 2b609bfc824..5f8c766c4be 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -26,7 +26,6 @@
#include "catalog/toasting.h"
#include "commands/alter.h"
#include "commands/async.h"
-#include "commands/cluster.h"
#include "commands/collationcmds.h"
#include "commands/comment.h"
#include "commands/conversioncmds.h"
@@ -46,6 +45,7 @@
#include "commands/proclang.h"
#include "commands/propgraphcmds.h"
#include "commands/publicationcmds.h"
+#include "commands/repack.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
diff --git a/src/include/commands/cluster.h b/src/include/commands/repack.h
similarity index 90%
rename from src/include/commands/cluster.h
rename to src/include/commands/repack.h
index d6b62c747e8..85061158b0c 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/repack.h
@@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
- * cluster.h
- * header file for postgres cluster command stuff
+ * repack.h
+ * header file for the REPACK command
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * src/include/commands/cluster.h
+ * src/include/commands/repack.h
*
*-------------------------------------------------------------------------
*/
-#ifndef CLUSTER_H
-#define CLUSTER_H
+#ifndef REPACK_H
+#define REPACK_H
#include "nodes/parsenodes.h"
#include "parser/parse_node.h"
@@ -52,4 +52,4 @@ extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
MultiXactId cutoffMulti,
char newrelpersistence);
-#endif /* CLUSTER_H */
+#endif /* REPACK_H */
--
2.47.3
--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="v50-0003-Add-CONCURRENTLY-option-to-REPACK-command.patch"
^ permalink raw reply [nested|flat] 275+ messages in thread
end of thread, other threads:[~2026-03-31 16:55 UTC | newest]
Thread overview: 275+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-24 04:09 [PATCH] Track skipped tables during autovacuum and autoanalyze Yugo Nagata <[email protected]>
2026-03-31 16:55 [PATCH v50 2/8] Rename cluster.c/h -> repack.c/h Álvaro Herrera <[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